2011-06-23 11 views
10

Jeśli okaże się to przydatne dla każdego, z chęcią przełożę go na wspólnotową wiki.Więcej "ziarnistość" z Mini profilera MVC

Mam kilka wolnych stron w aplikacji MVC3, a ponieważ wydaje mi się, że w moim kodzie niewiele czasu wykonania wydarzyło się, chciałem sprawdzić, czy mogę dowiedzieć się więcej o tym, co trwało tak długo. Nie, że mi się udało, ale po drodze zyskałem trochę więcej mądrości.

Nie ma tu niczego, co nie byłoby oczywiste dla nikogo z doświadczeniem MVC. Zasadniczo, tworzę własne ActionFilterAttribute, który wygląda tak:

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"]); 
    } 

To wydaje się działać dobrze, w moim przypadku nie dowiedział się, że przez większość czasu jest rzeczywiście spędził w części ResultExecuting życia, nie w moim działania.

Mam jednak kilka pytań na temat tego podejścia.

1) Czy jest to bezpieczny sposób robienia rzeczy? Zgaduję, nie, ponieważ filtr działania jest tworzony tylko raz, w metodzie RegisterGlobalFilters() w Global.asax.cs. Jeśli dwa żądania pojawią się jednocześnie, actionStep i resultStep będą bezwartościowe. Czy to prawda? Jeśli tak, czy ktoś, kto wie więcej niż ja, może w sprytny sposób poradzić sobie z tym? Działa dla mnie podczas lokalnego profilowania maszyn, ale prawdopodobnie nie jest tak dużo wdrożone na serwerze z wieloma osobami wysyłającymi żądania w tym samym czasie.

2) Czy istnieje sposób na uzyskanie lepszego wglądu w proces wykonywania wyników? A może powinienem zaakceptować, że renderowanie widoku itp. Zabiera czas? W mojej własnej aplikacji upewniam się, że cały dostęp do bazy danych został zakończony, zanim moja metoda działania zostanie zakończona (przy użyciu NHibernate Profiler w moim przypadku), a ja lubię utrzymywać moje widoki wąskie i proste; Każdy rodzaj wglądu w to, co spowalnia renderowanie może być jednak użyteczny. Myślę, że używając Mini Profiler w moich obiektach modelu pojawiłby się tutaj, gdyby został tutaj wykonany wolny kod z mojej strony.

3) Metody ResultDescriptor są prawdopodobnie złe i trujące. Pracowali dla mnie w moich testach, ale prawdopodobnie musieliby zostać zastąpieni przez coś solidniejszego. Po prostu poszedłem z pierwszymi wersjami, które dały mi coś w połowie użytecznego.

Wszelkie inne uwagi na ten temat również byłyby bardzo mile widziane, nawet jeśli brzmią "To zły pomysł, idźcie sami w spokoju".

+0

Jeśli chcesz, aby żądania były bezpieczne, dlaczego nie używasz zdarzeń aplikacji związanych z cyklem życia żądania? http://stackoverflow.com/a/24197984/3481183 – Believe2014

Odpowiedz

4

To wygląda na fajny pomysł. Uważam, że NIE jest to bezpieczny sposób robienia rzeczy.

Można połączyć go z HttpContext.Items jak ten

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

a następnie pobrać ją w podobny sposób

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

Oczywiście wprowadzenie swoich własnych kontroli na null i tak dalej.

Numer HttpContext jest inny dla każdego użytkownika/żądania.

To, o czym należy pamiętać, to: HttpContext.Current.Session.SessionID, w którym czasami zapominam, że jest to Identyfikator sesji bieżącego żądania HTTP (tj. Zmienia się za każdym razem, gdy trafia F5 lub w inny sposób składa nowe żądanie). Inną ważną rzeczą do zapamiętania jest to, że o każdej porze wszystkie wartości HttpContext.Current.Session.SessionID są z konieczności unikalne (tj. Jeden dla każdego użytkownika lub żądania), mogą być ponownie użyte, więc nie myśl o nich jako GUID-y, które są używane tylko raz każdy.

+1

To nie zadziała, jeśli użyjesz jakichkolwiek akcji podrzędnych i zostanie zarejestrowany jako filtr globalny; Otrzymasz błąd, gdy 'OnActionExecuting' zostanie wywołany na potomku, ponieważ' HttpContext.Items' będzie już zawierał element z kluczem '' actionstep "'. Możesz być w stanie wygenerować klucz z kombinacji kontrolera i nazwy akcji - to powinno być unikalne, ale nie testowałem tego. –

0

Możesz po prostu zawinąć metodę ExecuteCore na kontrolerze. :)

3

Istnieje już atrybut filtra akcji w zestawie MiniProfiler, który wykonuje profilowanie dla działań. Znajduje się w przestrzeni nazw StackExchange.Profiling.MVCHelpers i nazywa się ProfilingActionFilter. Możesz go rozszerzyć, aby również profilować swoje widoki.

Używa tego samego podejścia, co opisana przez @Dommer, ale zamiast bezpośrednio zapisywać IDisposable, przechowuje stos w HttpContext.Current.Items. Możesz zrobić to samo dla widoków.

Oto kod do profilowania Działanie:

/// <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(); 
    } 
} 

Nadzieja ta pomoc.

Powiązane problemy