2011-06-23 11 views
10

Si esto resulta útil para cualquier persona, con mucho gusto lo convertiré en una wiki comunitaria.Obteniendo más "granularidad" del MVC Mini Profiler

Tengo algunas páginas lentas en una aplicación MVC3, y dado que poco tiempo de ejecución parecía ocurrir en mi código, quería ver si podía averiguar más acerca de lo que llevó tanto tiempo. No es que lo haya logrado, pero obtuve un poco más de sabiduría en el camino.

No hay nada aquí que no sea obvio para cualquier persona con experiencia en MVC. Básicamente, he creado mi propia ActionFilterAttribute que tiene este aspecto:

public class ProfilerAttribute : ActionFilterAttribute 
{ 
    IDisposable actionStep = null; 
    IDisposable resultStep = null; 

    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
     actionStep = MiniProfiler.Current.Step("OnActionExecuting " + ResultDescriptor(filterContext)); 
     base.OnActionExecuting(filterContext); 
    } 

    public override void OnActionExecuted(ActionExecutedContext filterContext) 
    { 
     if (actionStep != null) 
     { 
      actionStep.Dispose(); 
      actionStep = null; 
     } 
     base.OnActionExecuted(filterContext); 
    } 

    public override void OnResultExecuting(ResultExecutingContext filterContext) 
    { 
     resultStep = MiniProfiler.Current.Step("OnResultExecuting " + ResultDescriptor(filterContext)); 
     base.OnResultExecuting(filterContext); 
    } 

    public override void OnResultExecuted(ResultExecutedContext filterContext) 
    { 
     if (resultStep != null) 
     { 
      resultStep.Dispose(); 
      resultStep = null; 
     } 
     base.OnResultExecuted(filterContext); 
    } 

    private string ResultDescriptor(ActionExecutingContext filterContext) 
    { 
     return filterContext.ActionDescriptor.ControllerDescriptor.ControllerName + "." + filterContext.ActionDescriptor.ActionName; 
    } 

    private string ResultDescriptor(ResultExecutingContext filterContext) 
    { 
     var values = filterContext.RouteData.Values; 

     return String.Format("{0}.{1}", values["controller"], values["action"]); 
    } 

Esto parece funcionar bien, y en mi caso he aprendido que la mayoría de las veces es en realidad pasó en la parte ResultExecuting de la vida, no dentro de mi comportamiento.

Sin embargo, tengo algunas preguntas sobre este enfoque.

1) ¿Es esta una forma segura de hacer las cosas? Supongo que no, ya que el filtro de acción se crea solo una vez, en el método RegisterGlobalFilters() en Global.asax.cs. Si aparecen dos solicitudes a la vez, actionStep y resultStep carecerán de valor. ¿Es esto cierto? Si es así, ¿puede alguien que sepa más que yo contribuir de una manera inteligente para manejar esto? Funciona para mí durante el perfilado de máquinas locales, pero probablemente no tanto implementado en un servidor con varias personas haciendo solicitudes al mismo tiempo.

2) ¿Hay alguna forma de obtener más información sobre el proceso de ejecución de resultados? ¿O debería simplemente aceptar que renderizar la vista, etc. toma el tiempo que lleva? En mi propia aplicación, me aseguro de que todo el acceso a la base de datos finalice antes de que se termine mi método de acción (usando NHibernate Profiler en mi caso), y me gusta mantener mi punto de vista esbelto y simple; Sin embargo, cualquier tipo de idea de lo que ralentiza la renderización podría ser útil. Supongo que usar Mini Perfilador en mi modelo de objetos aparecería aquí, si algún código lento de mi parte se ejecutara aquí.

3) Los métodos de ResultDescriptor son probablemente malos y venenosos. Han trabajado para mí en mis pruebas, pero probablemente necesitarían ser reemplazados por algo más sólido. Solo fui con las primeras versiones que me dieron algo a mitad de camino.

Cualquier otro comentario a esto también sería muy bienvenido, incluso si son "Esta es una mala idea, ve a morir solo".

+0

Si desea seguridad de la solicitud, ¿por qué no utiliza eventos de la aplicación relacionados con el ciclo de vida de la solicitud? http://stackoverflow.com/a/24197984/3481183 – Believe2014

Respuesta

4

Esto parece una buena idea. Creo que NO es una forma segura de hacer las cosas.

Se podría vincularlo a HttpContext.Items como esto

HttpContext.Items.Add("actionstep", actionStep); 
HttpContext.Items.Add("resultstep", resultStep); 

Y luego recuperarlo de forma similar

actionStep = HttpContext.Items["actionstep"]; 
resultStep = HttpContext.Items["resultstep"]; 

Obviamente poner en sus propios cheques por valores nulos y así sucesivamente.

El HttpContext es diferente para cada usuario/solicitud.

Lo que hay que recordar es HttpContext.Current.Session.SessionID que a veces me olvido de que es el SessionId de la solicitud HTTP actual (es decir, cambia cada vez que presiona F5 o hace una nueva solicitud). La otra cosa importante que hay que recordar es que, aunque a tiempo, todos los valores de HttpContext.Current.Session.SessionID son necesariamente únicos (es decir, uno para cada usuario o solicitud), se pueden reutilizar, así que no los piense como GUID que solo se utilizan una vez cada uno

+1

Esto no funcionará si usa acciones secundarias y está registrado como un filtro global; Recibirá un error cuando se llame a 'OnActionExecuting' en el niño ya que 'HttpContext.Items' ya tendrá un elemento con la tecla' "actionstep' '. Es posible que pueda generar la clave a partir de una combinación del controlador y el nombre de la acción, que debe ser única, pero no la he probado. –

0

Puede simplemente ajustar el método ExecuteCore en el controlador. :)

3

Ya existe un atributo de filtro de acción en el ensamblaje MiniProfiler que realiza el perfilado de las acciones. Está en el espacio de nombres StackExchange.Profiling.MVCHelpers y se llama ProfilingActionFilter. Puede extenderlo para también perfilar sus puntos de vista.

Utiliza el mismo enfoque descrito por @Dommer pero en lugar de almacenar el IDisposable directamente, almacena una pila en HttpContext.Current.Items. Puedes hacer lo mismo para las vistas.

Aquí está el código para el perfilado de acción:

/// <summary> 
/// This filter can be applied globally to hook up automatic action profiling 
/// 
/// </summary> 
public class ProfilingActionFilter : ActionFilterAttribute 
{ 
    private const string stackKey = "ProfilingActionFilterStack"; 

    /// <summary> 
    /// Happens before the action starts running 
    /// 
    /// </summary> 
    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
     if (MiniProfiler.Current != null) 
     { 
      Stack<IDisposable> stack = HttpContext.Current.Items[(object) "ProfilingActionFilterStack"] as Stack<IDisposable>; 
      if (stack == null) 
      { 
       stack = new Stack<IDisposable>(); 
       HttpContext.Current.Items[(object) "ProfilingActionFilterStack"] = (object) stack; 
      } 
      MiniProfiler current = MiniProfiler.Current; 
      if (current != null) 
      { 
       RouteValueDictionary dataTokens = filterContext.RouteData.DataTokens; 
       string str1 = !dataTokens.ContainsKey("area") || string.IsNullOrEmpty(dataTokens["area"].ToString()) ? "" : (string) dataTokens["area"] + (object) "."; 
       string str2 = Enumerable.Last<string>((IEnumerable<string>) filterContext.Controller.ToString().Split(new char[1] { '.' })) + "."; 
       string actionName = filterContext.ActionDescriptor.ActionName; 
       stack.Push(MiniProfilerExtensions.Step(current, "Controller: " + str1 + str2 + actionName, ProfileLevel.Info)); 
      } 
     } 
     base.OnActionExecuting(filterContext); 
    } 

    /// <summary> 
    /// Happens after the action executes 
    /// 
    /// </summary> 
    public override void OnActionExecuted(ActionExecutedContext filterContext) 
    { 
     base.OnActionExecuted(filterContext); 
     Stack<IDisposable> stack = HttpContext.Current.Items[(object) "ProfilingActionFilterStack"] as Stack<IDisposable>; 
     if (stack == null || stack.Count <= 0) return; 
     stack.Pop().Dispose(); 
    } 
} 

Esperanza esta ayuda.

Cuestiones relacionadas