页面生成时间 - ASP.Net MVC

15
我正在寻找一种跟踪服务器生成页面所需时间的方法。我知道可以使用 Trace 来跟踪此项信息,但我需要一种按页面显示此信息的方法。
这是 ASP.Net MVC 2。
3个回答

16

你可以像ActionFilterAttribute一样实现它。

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class LoggingAttribute : ActionFilterAttribute
{
    private readonly Stopwatch _sw;

    public LoggingAttribute()
    {
        _sw = new Stopwatch();
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        _sw.Start();
        Debug.WriteLine("Beginning executing: " + GetControllerAndActionName(filterContext.ActionDescriptor));
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        _sw.Stop();
        var ms = _sw.ElapsedMilliseconds;

        Debug.WriteLine("Finishing executing: " + GetControllerAndActionName(filterContext.ActionDescriptor));
        Debug.WriteLine("Time elapsed: "+ TimeSpan.FromMilliseconds(ms).TotalSeconds);
    }

    private string GetControllerAndActionName(ActionDescriptor actionDescriptor)
    {
        return actionDescriptor.ControllerDescriptor.ControllerName + " - " + actionDescriptor.ActionName;
    }
}

使用装饰器将其应用到每个控制器或操作方法中,然后就可以轻松查看调试输出的文本。

编辑:如果你想在页面上打印它,你可以将这段代码添加到OnActionExecuted方法中。

if(filterContext.Result is ViewResult) { //Make sure the request is a ViewResult, ie. a page
  ((ViewResult) filterContext.Result).ViewData["ExecutionTime"] = ms; //Set the viewdata dictionary
}

现在你已经将执行时间保存在ViewData中,可以在页面中访问它。我通常会像这样将它放在主页面中:

<!-- The page took <%= ViewData["ExecutionTime"] %> ms to execute -->

4
在我的项目中,所有的控制器都继承自一个基础控制器(BaseController)。因此,我只需要装饰(decorate)这个基础控制器,进而使得执行的所有动作方法都被“装饰”了。 - Kenny Eliasson
2
这是一个相当不错的方法,只值得一提的是,它实际上跟踪的是控制器的生命周期,而不是整个请求的生命周期。 - JOBG
1
@Pino - 在ASP.NET MVC3中,你可以在Application_Start处理程序中将属性添加到GlobalFiltersCollection中,以避免在每个类/操作上都进行装饰。 - Justin Rusbatch
2
应该在 OnResultExecuted 中计算经过的时间,而不是在 OnActionExecuted 中计算吗? - BigJoe714
我同意 @BigJoe714 - Kat Lim Ruiz
显示剩余5条评论

14

是的,Derin建议是在ASP.NET应用程序中执行操作的标准方式,我建议添加一个if条件语句,以便它不会干扰非HTML响应:

编辑:添加完整实现

public class PerformanceMonitorModule : IHttpModule
{

    public void Init(HttpApplication context)
    {
        context.PreRequestHandlerExecute += delegate(object sender, EventArgs e)
        {
            //Set Page Timer Star
            HttpContext requestContext = ((HttpApplication)sender).Context;
            Stopwatch timer = new Stopwatch();
            requestContext.Items["Timer"] = timer;
            timer.Start();
             };
        context.PostRequestHandlerExecute += delegate(object sender, EventArgs e)
        {

            HttpContext httpContext = ((HttpApplication)sender).Context;
            HttpResponse response = httpContext.Response;
            Stopwatch timer = (Stopwatch)httpContext.Items["Timer"];
            timer.Stop();

            // Don't interfere with non-HTML responses
            if (response.ContentType == "text/html")
            {
                double seconds = (double)timer.ElapsedTicks / Stopwatch.Frequency;
                string result_time = string.Format("{0:F4} sec ", seconds);
                RenderQueriesToResponse(response,result_time);
            }
        };
    }
    void RenderQueriesToResponse(HttpResponse response, string result_time)
    {
        response.Write("<div style=\"margin: 5px; background-color: #FFFF00\"");
        response.Write(string.Format("<b>Page Generated in "+ result_time));
        response.Write("</div>");
    }
    public void Dispose() { /* Not needed */ }
}

您还可以为它添加一些样式...

并且请记得在httpModules部分的WebConfig中注册您的模块:

  <add name="Name" type="namespace, dll"/>

要完整参考这个内容,请查看Steven Sanderson的《Pro ASP.NET MVC Framework》第15章——性能,监测页面生成时间。

编辑:(评论@Pino) 以下是我项目的示例: alt text http://www.diarioplus.com/files/pictures/example_performance.JPG


谢谢 - 运行得很好,除了这个似乎在</html>元素之后输出,这破坏了我的页面 :( 有什么建议吗? - LiamB
这是作为响应的一部分添加(追加)的,它可能不在HTML结束标记之后。 - JOBG
我编辑了我的帖子,并附上了一个示例图片。我已经有一个正在运行并进行测试的项目,但我不知道为什么Darin说它是在HTML之后...也许他指的是另一种实现方式。 - JOBG
你能发布完整的示例吗?目前的代码在</html>标签下方显示。 - LiamB

4

这将取决于您想在哪里包含此信息。例如,您可以编写一个http处理程序,在</html>标记后显示呈现时间:

public class RenderTimeModule : IHttpModule
{
    public void Init(HttpApplication context)
    {
        context.BeginRequest += (sender, e) =>
        {
            var watch = new Stopwatch();
            var app = (HttpApplication)sender;
            app.Context.Items["Stopwatch"] = watch;
            watch.Start();
        };

        context.EndRequest += (sender, e) =>
        {
            var app = (HttpApplication)sender;
            var watch = (Stopwatch)app.Context.Items["Stopwatch"];
            watch.Stop();
            var ts = watch.Elapsed;
            string elapsedTime = String.Format("{0} ms", ts.TotalMilliseconds);
            app.Context.Response.Write(elapsedTime);
        };
    }

    public void Dispose()
    {
    }
}

如果你想在html页面的中间显示渲染时间,那么这个渲染时间不会计算总页面渲染时间。


这似乎输出在</html>元素之后,这破坏了我的页面:( 有什么建议吗? - LiamB
是的,那就是我在帖子中说的话。如果您不想破坏验证,可以将其放在评论中。 - Darin Dimitrov
这种方法适用于Json和文件结果吗,还是只适用于html? - Jace Rhea

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接