Page Instrumentation in ASP.NET 4.5

        Introduction:


                   ASP.NET 4.5 include a hidden gem called Page Instrumentation, very few people aware of this gem. You can use page instrumentation in ASP.NET 4.5 WebForm and MVC 5 or 4(assuming it targets 4.5). It allows you to inspect/instrument a web form or a mvc view during the rendering process. Page instrumentation are useful in scenarios when you have some performance issues regarding view engine rendering. In this article, I will show you how to use this feature in ASP.NET 4.5.


        Description:

 

 

                    To start instrumenting a page, you need to inherit PageExecutionListener class,


        
    public abstract class PageExecutionListener
    {
        protected PageExecutionListener();
        public abstract void BeginContext(PageExecutionContext context);
        public abstract void EndContext(PageExecutionContext context);
    }

                    The BeginContext will be called by a view engine before it renders the output for the specified context. Similarly, EndContext called by a view engine after it renders the output for the specified context. Both of these methods accept PageExecutionContext class as a parameter which have the following members,


    
    public class PageExecutionContext
    {
        public PageExecutionContext();
        public bool IsLiteral { get; set; }
        
        public int Length { get; set; }
       
        public int StartPosition { get; set; }
        
        public TextWriter TextWriter { get; set; }
        
        public string VirtualPath { get; set; }
    }


                    All the properties of PageExecutionContext class are self explanatory. Let assume we have the following MVC view(for understanding how many times and when the BeginContext and EndContext will be invoke by the framework),

 

        
    @{
        Layout = null;
    }
    <!DOCTYPE html>
    <html>
    <head>
        <meta name="viewport" content="width=device-width" />
        <title>Home</title>
    </head>
    <body>
        <div>
        </div>
    </body>
    </html>

 

 

                    The Razor View Engine will emit the following C# code,

 

 

    public class _Page_Views_Home_Index_cshtml : System.Web.Mvc.WebViewPage<dynamic>
    {
        public _Page_Views_Home_Index_cshtml()
        {
         }
         
         protected ASP.global_asax ApplicationInstance {
             get {
                 return ((ASP.global_asax)(Context.ApplicationInstance));
             }
         }
         public override void Execute()
         {
             
             Layout = null;
             
            #line default
            #line hidden
             BeginContext("~/Views/Home/Index.cshtml", 25, 48, true);
             WriteLiteral("\r\n\r\n<!DOCTYPE html>\r\n\r\n<html>\r\n<head>\r\n    <meta");
             EndContext("~/Views/Home/Index.cshtml", 25, 48, true);
             BeginContext("~/Views/Home/Index.cshtml", 73, 16, true);
             WriteLiteral(" name=\"viewport\"");
             EndContext("~/Views/Home/Index.cshtml", 73, 16, true);
             BeginContext("~/Views/Home/Index.cshtml", 89, 29, true);
             WriteLiteral(" content=\"width=device-width\"");
             EndContext("~/Views/Home/Index.cshtml", 89, 29, true);
             BeginContext("~/Views/Home/Index.cshtml", 118, 59, true);
             WriteLiteral(" />\r\n    <title>Home</title>\r\n</head>\r\n<body>\r\n    <div> \r\n");
             EndContext("~/Views/Home/Index.cshtml", 118, 59, true);
             BeginContext("~/Views/Home/Index.cshtml", 177, 8, true);
             WriteLiteral("        ");
             EndContext("~/Views/Home/Index.cshtml", 177, 8, true);
             BeginContext("~/Views/Home/Index.cshtml", 186, 2, false);

#line default
#line hidden
             EndContext("~/Views/Home/Index.cshtml", 186, 2, false);
             BeginContext("~/Views/Home/Index.cshtml", 188, 32, true);
             WriteLiteral("\r\n    </div>\r\n</body>\r\n</html>\r\n");
             EndContext("~/Views/Home/Index.cshtml", 188, 32, true);
         }
     }

                   

 

                    Finally, here is a simple implementation of PageExecutionListener class which calculates the total time each time whenever something is written to the output response,

 

 

        protected void Application_BeginRequest()
        {
            Context.PageInstrumentation.ExecutionListeners.Add(new MyPageExecutionListener());
        }
        public class MyPageExecutionListener : PageExecutionListener
        {
            Stopwatch s = new Stopwatch();
            public override void BeginContext(PageExecutionContext context)
            {
                s.Start();
            }
            public override void EndContext(PageExecutionContext context)
            {
                s.Stop();
                var e = string.Format("It takes {0}", s.Elapsed.ToString());
                Trace.WriteLine(e);
                s.Reset();
            }
        }

 

 

        Summary:


                    In this article, I showed you how to use Page Instrumentation, a new  feature in ASP.NET 4.5 which might be useful for you during debugging. Hopefully you enjoyed my this article too.



3 Comments

  • Can PageInstrumentation show us more details than a tool like glimpse or the MVC MiniProfiler?

    BTW: AFAIK Glimpse works with WebForms lately...

    http://getglimpse.com/

  • @PeterGfader, No, But Glimpse and MiniProfiler can use this feature. Beacuse it is natively supported.

  • I know that the BrowserLink sourcemaps uses this feature to map html elements rendered by WebForms and WebPages back to its original sourcefiles. Is it possible for you to write a blogpost on this subject? I'm interested in instrumenting my own html generation so BrowserLink can include it in its sourcemap.

Comments have been disabled for this content.