EPiServer and MvcMiniProfiler - Profiled Base Classes

by Greg 11. September 2011 11:00

So far we've had a basic introduction to MvcMiniProfiler and how to get it working with EPiServer, we've also looked at a version of DataFactory that gives profiling data about its calls.

This time I'm going to look at giving easier access to stepping the profiler, not that it's exactly hard to begin with, as well as building some basic profiling into some base classes so that there is always a minimum of profiling in your pages.

This is all work in progress or thinking in code if you will. It hasn't been road tested much and I'm not sure it's the right recipe, so please feel free to leave feedback in the comments.

Stepping The Profiler

Stepping the profiler is usually achieved with a call to MvcMiniProfiler.MiniProfiler.StepStatic or MvcMiniProfiler.MiniProfiler.Current.Step. This might get a bit bloated so let's see if we can shorten it some. As I see it, there are three places we're going to want to step the profiler from:

  1. Page Templates
  2. Master Pages
  3. User Controls

So what about hanging a Step method off each of these types:

 

using System;
using System.Web.UI;
using MvcMiniProfiler;

namespace EPiServer.MiniProfiler
{
    public static class ProfilerExtensions
    {
        public static IDisposable StepProfilerEx(this PageBase page, string name, ProfileLevel level = ProfileLevel.Info)
        {
            string typeName = page.GetType().Name;
            return MvcMiniProfiler.MiniProfiler.StepStatic(string.Format("{0} {1}", typeName, name), level);
        }

        public static IDisposable StepProfilerEx(this MasterPage page, string name, ProfileLevel level = ProfileLevel.Info)
        {
            string typeName = page.GetType().Name;
            return MvcMiniProfiler.MiniProfiler.StepStatic(string.Format("{0} {1}", typeName, name), level);
        }

        public static IDisposable StepProfilerEx(this UserControl control, string name, ProfileLevel level = ProfileLevel.Info)
        {
            string typeName = control.GetType().Name;
            return MvcMiniProfiler.MiniProfiler.StepStatic(string.Format("{0} {1}", typeName, name), level);
        }
    }
}

Here I've added three Extension Methods to a class which combines the type name of the caller along with the user-defined step name. This gives us a clue as to where the code is being executed, the resultant profile looks something like this:

Profiled Base Classes 

My next idea is to provide profiled versions of the page events such as OnLoad and OnPreRender as well as profiled versions of the EPiServer API methods like PageBase.GetPage. The full code can be downloaded in this zip file, but for illustrative purposes part of my ProfiledTemplatePage might look like this:

 

using System;
using System.Web.UI.WebControls;
using EPiServer.Core;
using MvcMiniProfiler;

namespace EPiServer.MiniProfiler
{
    public abstract class ProfiledTemplatePage : TemplatePage
    {
        private string typeName;

        public MvcMiniProfiler.MiniProfiler Profiler
        {
            get { return MvcMiniProfiler.MiniProfiler.Current; }
        }

        public ProfiledTemplatePage()
        {
            // Store the name of the current type for profiling steps.
            typeName = this.GetType().Name;
        }

        public IDisposable StepProfiler(string name, ProfileLevel level = ProfileLevel.Info)
        {
            return Profiler.Step(string.Format("{0} {1}", typeName, name), level);
        }

        #region Profiled events

        protected virtual void OnLoadProfiled(EventArgs e) { }

        protected virtual void OnLoadCompleteProfiled(EventArgs e) { }

        protected internal virtual void OnPreRenderProfiled(EventArgs e) { }

        // Other profiled events here...

        #endregion

        #region PageBase overrides

        public override PageData GetPage(PageReference pageLink)
        {
            using (StepProfiler("GetPage"))
                return base.GetPage(pageLink);
        }

        // Other profiled EPiServer methods here...

        #endregion

        #region Control overrides

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            using (StepProfiler("OnLoadProfiled"))
                OnLoadProfiled(e);
        }

        protected override void OnLoadComplete(EventArgs e)
        {
            base.OnLoadComplete(e);
            using (StepProfiler("OnLoadCompleteProfiled"))
                OnLoadCompleteProfiled(e);
        }

        // Wrap other events here...

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            using (StepProfiler("OnPreRenderProfiled"))
                OnPreRenderProfiled(e);

            // Logic to add Profiler Includes to the <head> of the page. 
            string html = MvcMiniProfiler.MiniProfiler.RenderIncludes().ToHtmlString();
            Page.Header.Controls.Add(new Literal
            {
                Mode = LiteralMode.PassThrough,
                Text = html
            });
        }

        #endregion
    }
}

This is a pretty big code snippet as it stands so I've left out many of the events and methods, it's worth noting that OnPreRender wraps the OnPreRenderProfiled method but then adds the profiler includes to the <head> of the page. There are similar classes in the form of ProfiledUserControl and ProfiledMasterPage, ProfiledTemplatePage is the only one to render the includes to the head.

Using the code is pretty obvious, you inherit from the base class and override the Profiled versions of events:

using System;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using EPiServer.Core;
using EPiServer.MiniProfiler;

namespace EPiServer_MiniProfiler.Templates
{
    public partial class News : ProfiledTemplatePage
    {
        protected override void OnLoadProfiled(EventArgs e)
        {
            NewsItems.DataSource = GetChildren(CurrentPageLink);
            using (StepProfiler("Binding news list"))
                NewsItems.DataBind();
        }
    }
}

 

Work In Progress

I'm not 100% about this implementation as it's not a like-for-like replacement for TemplatePage. It might be easy for a developer who is new to the project to simply use OnLoad rather than OnLoadProfiled. The other thing is that Auto-Wireup events such as Page_Load are triggered within the built-in OnLoad events. This raises the idea of implementing a Page_LoadProfiled but I'm not sure this isn't just throwing good code after bad.

protected virtual void OnLoadProfiled(EventArgs e)
{
    CallEvent("Page_LoadProfiled", e);
}

private void CallEvent(string eventName, EventArgs e)
{
    var t = this.GetType();
    var method = t.GetMethod(eventName);
    if (method != null)
        method.Invoke(this, new[] { e });
}

As I've said, these are some first thoughts on building profiling data into your EPiServer working practices and I haven't tried them out enough to be sure of their worth.

Next Time

Next time we'll look at locking down the trace information so that it's only visible to the people you want to see it.

Tags: , , ,

ASP.NET | EPiServer

Pingbacks and trackbacks (1)+

Add comment




  Country flag
biuquote
  • Comment
  • Preview
Loading