获得更多“粒度”来自 MVC 迷你分析器
如果这对任何人都有用,我很乐意将其变成社区维基。
我的 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 方法可能是邪恶且有毒的。它们在我的测试中为我工作,但可能需要被更强大的东西取代。我只是选择了第一个版本,它给了我一些有用的东西。
对此的任何其他评论也将非常受欢迎,即使他们是“这是一个坏主意,独自死去”。
Should this turn out to be useful for anyone, I'll gladly turn it into a community wiki thing.
I have some slow pages in an MVC3 app, and since little of the execution time seemed to happen in my code, I wanted to see if I could find out more about what took so long. Not that I succeeded, but I gained a little more wisdom along the way.
There is nothing here that isn't obvious to anyone with some MVC experience. Basically, I created my own ActionFilterAttribute that looks like this:
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"]);
}
This seems to work well, and in my case I have learned that most of the time is actually spent in the ResultExecuting part of life, not inside my actions.
However, I have some questions about this approach.
1) Is this a request-safe way of doing things? I am guessing no, since the actionfilter is created only once, in the RegisterGlobalFilters() method in Global.asax.cs. If two requests appear at once, actionStep and resultStep will be worthless. Is this true? If so, can someone who knows more than me contribute a clever way to handle this? Works for me during local machine profiling, but probably not so much deployed on a server with multiple people making requests at the same time.
2) Is there any way to get more insight into the result-executing process? Or should I just accept that rendering the view etc. takes the time it takes? In my own app I ensure that all database access is finished before my action method is over (using NHibernate Profiler in my case), and I like to keep my views slim and simple; Any kind of insight into what slows the rendering down could still be useful, though. I guess using the Mini Profiler in my model objects would show up here, if any slow code on my part was executed here.
3) The ResultDescriptor methods are probably evil and poisonous. They've worked for me in my tests, but would probably need to be replaced by something more robust. I just went with the first versions that gave me something halfway useful.
Any other comments to this would also be very welcome, even if they are "This is a bad idea, go die alone".
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
这看起来是一个很酷的主意。我相信这不是一种请求安全的做事方式。
您可以像这样将其链接到
HttpContext.Items
然后以类似的方式检索它
显然放入您自己的空值检查等。
每个用户/请求的
HttpContext
都是不同的。关于 HttpContext.Current.Session.SessionID 需要记住的一点是,我有时会忘记它是当前 HTTP 请求的 SessionId(即每次按 F5 或以其他方式发出新请求时它都会更改) 。另一件需要记住的重要事情是,在任何时间,所有 HttpContext.Current.Session.SessionID 值都必须是唯一的(即每个用户或请求一个),它们可以重复使用,因此,不要将它们视为仅使用一次的 GUID。
This looks like a cool idea. I believe that it's NOT a request safe way of doing things.
You could link it to
HttpContext.Items
like thisAnd then retrieve it in similar fashion
Obviously putting in your own checks for nulls and so forth.
The
HttpContext
is different for each user/request.The thing to remember about
HttpContext.Current.Session.SessionID
which I sometimes forget it that it is the SessionId of the current HTTP request (i.e. it changes each time you hit F5 or otherwise make a new request). The other important thing to remember is that, whilst at any on time, allHttpContext.Current.Session.SessionID
values are necessarily unique (i.e. one for each user, or request), they can be reused, so dno't think of them as GUIDs which are only used once each.MiniProfiler 程序集中已有一个操作筛选器属性,用于对操作进行分析。它位于 StackExchange.Profiling.MVCHelpers 命名空间中,称为 ProfilingActionFilter。您还可以扩展它来分析您的观点。
它使用与 @Dommer 描述的相同方法,但不是直接存储 IDisposable,而是将 Stack 存储在 HttpContext.Current.Items 中。您可以对视图执行相同的操作。
以下是操作分析的代码:
希望有所帮助。
There is already an action filter attribute in the MiniProfiler assembly that does the profiling for the actions. It is in the StackExchange.Profiling.MVCHelpers namespace and it's called ProfilingActionFilter. You can extend it to also profile your views.
It uses the same approach as described by @Dommer but instead of storing the IDisposable directly, it stores a Stack in the HttpContext.Current.Items. You can do the same for the views.
Here is the code for the action profiling:
Hope this help.
您可以将 ExecuteCore 方法包装在 Controller 上。 :)
You can just wrap ExecuteCore method on Controller. :)