如果这对任何人都有用,我很乐意把它变成一个社区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),并且我喜欢保持我的视图简洁明了;不过,任何一种关于渲染速度减慢的见解都可能是有用的。我想在我的模型对象中使用MiniProfiler会显示在这里,如果我的部分有任何慢代码在这里执行的话。
3) ResultDescriptor方法可能是邪恶和有毒的。它们在我的测试中对我有效,但可能需要用更强大的东西来取代。我只是选择了第一个版本,它给了我一些有用的东西。
任何其他对此的评论也将非常受欢迎,即使他们是"这是个坏主意,孤军奋战"。
这看起来是个不错的主意。我认为这不是一种请求安全的做事方式。
你可以像这个一样把它链接到HttpContext.Items
HttpContext.Items.Add("actionstep", actionStep);
HttpContext.Items.Add("resultstep", resultStep);
然后以类似的方式检索
actionStep = HttpContext.Items["actionstep"];
resultStep = HttpContext.Items["resultstep"];
很明显,你自己检查null等等。
HttpContext
对于每个用户/请求是不同的。
关于HttpContext.Current.Session.SessionID
需要记住的一点是,我有时会忘记它是当前HTTP请求的SessionId(即,每次按下F5或发出新请求时,它都会更改)。另一件需要记住的重要事情是,尽管在任何时候,所有HttpContext.Current.Session.SessionID
值都必须是唯一的(即每个用户或请求一个),但它们可以重复使用,所以不要把它们看作每个只使用一次的GUID。
MiniProfiler程序集中已经有一个操作筛选器属性,用于对操作进行分析。它位于StackExchange.Profiling.MVCHelpers命名空间中,名为ProfileActionFilter。您可以对其进行扩展,也可以对视图进行纵断面分析。
它使用与@Dommer描述的方法相同的方法,但不是直接存储IDisposable,而是在HttpContext.Current.Items中存储一个Stack。您可以对视图执行同样的操作。
以下是动作评测的代码:
/// <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();
}
}
希望得到帮助。
您可以将ExecuteCore方法包装在Controller上。:)