【问题标题】:Getting more "granularity" from the MVC Mini Profiler从 MVC Mini Profiler 获得更多“粒度”
【发布时间】:2011-06-23 07:42:19
【问题描述】:

如果这对任何人都有用,我很乐意将它变成社区 wiki 的东西。

我在一个 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 只在 Global.asax.cs 的 RegisterGlobalFilters() 方法中创建一次。如果同时出现两个请求,actionStep 和 resultStep 将一文不值。这是真的?如果是这样,比我了解更多的人可以提供一个聪明的方法来处理这个问题吗?在本地机器分析期间为我工作,但可能没有那么多部署在多人同时发出请求的服务器上。

2) 有什么方法可以更深入地了解结果执行过程?还是我应该接受渲染视图等需要花费时间?在我自己的应用程序中,我确保在我的操作方法结束之前完成所有数据库访问(在我的情况下使用 NHibernate Profiler),并且我喜欢保持我的视图简洁明了;不过,任何关于减慢渲染速度的见解仍然是有用的。我猜在我的模型对象中使用 Mini Profiler 会显示在这里,如果我在这里执行了任何慢代码。

3) ResultDescriptor 方法可能是邪恶的和有毒的。它们在我的测试中为我工作,但可能需要用更强大的东西代替。我只是选择了第一个版本,它给了我一些有用的东西。

对此的任何其他 cmets 也将受到欢迎,即使他们是“这是个坏主意,独自去死”。

【问题讨论】:

标签: asp.net-mvc mvc-mini-profiler


【解决方案1】:

这看起来是个很酷的主意。我相信这不是一种请求安全的做事方式。

你可以像这样链接到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 要记住的一点是,我有时会忘记它是当前 HTTP 请求的 SessionId(即,每次按下 F5 或以其他方式发出新请求时它都会改变)。要记住的另一件重要的事情是,尽管在任何时候,所有HttpContext.Current.Session.SessionID 值都必须是唯一的(即每个用户或请求一个),它们可以重复使用,所以不要将它们视为 GUID,它们是每个只用一次。

【讨论】:

  • 如果您使用任何子操作并且它已注册为全局过滤器,这将不起作用;当OnActionExecuting 被孩子调用时,您将收到一个错误,因为HttpContext.Items 已经有一个带有"actionstep" 键的项目。您可能能够从控制器和操作名称的组合中生成密钥 - 这应该是唯一的,但我没有测试它。
【解决方案2】:

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

希望对您有所帮助。

【讨论】:

    【解决方案3】:

    你可以在 Controller 上封装 ExecuteCore 方法。 :)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-30
      • 1970-01-01
      • 2011-10-13
      相关资源
      最近更新 更多