Получение большей «детализации» от MVC Mini Profiler - PullRequest
11 голосов
/ 23 июня 2011

Если это окажется полезным для всех, я с удовольствием превращу это в вики-сообщество.

У меня есть несколько медленных страниц в приложении MVC3, и поскольку в моем коде, похоже, мало времени выполнения, я хотел посмотреть, смогу ли я узнать больше о том, что заняло так много времени. Не то чтобы мне это удалось, но я приобрел немного больше мудрости на этом пути.

Здесь нет ничего, что не было бы очевидно для любого, имеющего некоторый опыт работы с MVC. По сути, я создал свой собственный ActionFilterAttribute, который выглядит следующим образом:

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

Кажется, это работает хорошо, и в моем случае я узнал, что большую часть времени фактически проводит часть ResultExecuting, а не мои действия.

Однако у меня есть несколько вопросов об этом подходе.

1) Это безопасный для запроса способ ведения дел? Я предполагаю, нет, так как actionfilter создается только один раз, в методе RegisterGlobalFilters () в Global.asax.cs. Если два запроса появляются одновременно, actionStep и resultStep будут бесполезны. Это правда? Если так, может ли кто-нибудь, кто знает больше меня, предложить умный способ справиться с этим? У меня работает во время профилирования локальной машины, но, возможно, не так много развернуто на сервере, когда несколько человек делают запросы одновременно.

2) Есть ли способ получить более полное представление о процессе выполнения результата? Или я должен просто согласиться с тем, что рендеринг вида и т. Д. Требует времени? В моем собственном приложении я гарантирую, что весь доступ к базе данных будет завершен до того, как мой метод действия будет завершен (с использованием NHibernate Profiler в моем случае), и мне нравится сохранять свои взгляды тонкими и простыми; Любое понимание того, что замедляет рендеринг, может быть полезным. Я предполагаю, что использование Mini Profiler в моих модельных объектах появилось бы здесь, если бы здесь выполнялся медленный код с моей стороны.

3) Методы ResultDescriptor, вероятно, злые и ядовитые. Они работали на меня в моих тестах, но, вероятно, нужно было бы заменить что-то более надежное. Я просто пошел с первыми версиями, которые дали мне что-то наполовину полезное.

Любые другие комментарии к этому также будут приветствоваться, даже если они "Это плохая идея, иди умри в одиночестве".

Ответы [ 3 ]

5 голосов
/ 23 июня 2011

Это похоже на классную идею. Я считаю, что это НЕ безопасный способ запроса.

Вы можете связать его с HttpContext.Items вот так

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

А затем получить его аналогичным образом

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

Очевидно, что вы вводите свои собственные проверки для нулей и т. Д.

HttpContext отличается для каждого пользователя / запроса.

Что нужно помнить о HttpContext.Current.Session.SessionID, о котором я иногда забываю, что это SessionId текущего HTTP-запроса (то есть он меняется каждый раз, когда вы нажимаете F5 или иным образом делаете новый запрос). Еще одна важная вещь, которую следует помнить, это то, что хотя в любое время все значения HttpContext.Current.Session.SessionID обязательно являются уникальными (т. Е. Одно для каждого пользователя или запроса), их можно использовать повторно, поэтому не думайте о них как о GUID, которые используется только один раз каждый.

4 голосов
/ 07 мая 2012

В сборке MiniProfiler уже есть атрибут фильтра действий, который выполняет профилирование для действий.Он находится в пространстве имен StackExchange.Profiling.MVCHelpers и называется ProfilingActionFilter.Вы можете расширить его, чтобы также профилировать свои представления.

Он использует тот же подход, который описан @Dommer, но вместо непосредственного хранения IDisposable он сохраняет стек в HttpContext.Current.Items.Вы можете сделать то же самое для представлений.

Вот код для профилирования действия:

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

Надеюсь, эта помощь.

0 голосов
/ 03 июля 2011

Вы можете просто обернуть метод ExecuteCore на контроллере. :)

...