Greg Brant

Triathlon and The Web

Windows Phone 7 from scratch - Data Binding and Navigation - Part 3

Passing Data Between Pages

While the basics of navigation are straight forward, just calls to NavigationService.Navigate(...), wiring them up in the MVVMLight stylee was not as straight forward, especially as MVVMLight has changed since Jesse wrote the articles in 2011. Luckily I was able to take the sample code from Soup to Nuts #5 and get it working.

Once this was working, the next problem is notifying the destination page what data was clicked on the original page so that it can display the details of that data. A quick google revealed this stackoverflow question which has a few useful links.

One of the links suggests Caliburn.Micro might be capable of passing this data but I chose note to persue this at this time. I'm already knee deep in XAML and MVVMLight, the last thing I need right now is another framework. The other method is to pass some identifier on the querystring when navigating and being a web developer, this is really familiar so I'm going to roll with this for now.

This is easily achieved by hooking in to the OnNavigatedTo event handler of the System.Windows.Controls.Page type. I'm not convinced this is the MVVM way of doing this so I need to revisit this later as right now, I'm more interested in getting this wired up.

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);

    string foodName = this.NavigationContext.QueryString["FoodType"];
    var locator = App.Current.Resources["Locator"] as ViewModelLocator;
    var food = locator.Today.Foods.Single(x => x.Name == foodName);

    this.DataContext = food;
}

Data Binding

My ViewModel for the Today screen is a collection of FoodTypeScores each with their own integer score and a TotalScore that sums the individual scores. Databinding is a fairly easy concept and I didn't really have any issues getting the total displayed on the header of the page and binding the collection of FoodTypeScoresto a ListBox. 

Windows Phone 7 from scratch - First Steps and Test Infrastructure - Part 2

I actually started this project a week or so before and had a bit of a play with data binding and figured out the logic working for how I'm going to have the food scoring work. I'd originaly started with a vanilla Windows Phone Application project, the development felt a bit fast and loose and I was aware that my code maybe wasn't best practice and it definitely wasn't under test.

Adding Some Structure

Although I've never done any Silverlight development I'm aware of it and I'v heard various podcasts and read a few blog posts, enough to know people tend to use the MVVM pattern. I remmebered that I've had Jesse Liberty's blog in Google reader for a couple of years now and never really taken much notice about it, it wasn't massively relevant to an ASP.NET developer. 

I quick scout around revealed a five-parter on the MVVMLight toolkit.

  1. MVVM Light Toolkit: Soup To Nuts Part I
  2. MVVM Light Toolkit Soup To Nuts Part 2
  3. MVVM Light Toolkit Soup To Nuts 3
  4. MVVM Light Soup To Nuts Part 4
  5. MVVM Light Soup To Nuts Part 5

This gave me a good feel for the pattern and soem further reading, including the tutorials lined from Laurent's MVVMLight site, the rest of Jesse's Windows Phone From Scratch series and probably all the other content on his site.

Having read that I felt MVVMLight was the way for me to go for this first project and so I put what I'd done to one side and started over with a fresh MVVMLight project. 

Unit Testing

Before I went wading into the implementation of the application, I wanted to get the rest of the infrastructure set up to enable me to do things right. 

A bit of Googling revealed the Silverlight testing framework from Jeff Wilcox which looked promising but then I hit Gold! Etienne Giust has an excellent from-scratch guide on unit testing on Windows Phone 7.5. He takes you through every step required to get you up and running. 

With that, I moved my FoodTypeScore into my new MVVMLight application and got it under test. 

I've now got my environment set up and a firmer grasp of how the app is going to be pieced together, as well as some working code and seven passing unit tests!

Next time I'm going to implement the home screen of the app.

My first Windows Phone 7 app: Diet Quality Score Tracker - Part 1

I've been wanting to write a Windows Phone 7 app sine I got my Lumia 800 at the Nokia and onedotzero AlphaLabs event back in November but I could never get it off the ground. The problem with wanting to write something like this is never learning the technology, it's finding the subject matter, and I could never find an idea that I was bothered enough about that I would keep up the effort after two or three days. but now I'm past that point, I've found my subject, I'm going to do a triathlon and as part of my training I'm going to get fitter and closer to m Racing Weight. 

What's your Racing Weight?

Nobody knows exactly until you find it, it's a bit of a catch 22 but one thing you can do is take an educated guess, change some things in your lifestyle and start working towards that guess. One of these things is the quality of your diet and is tracked using a daily Diet Quality Score, and this is what my app will allow you to track.

DQS

The Diet Quality Score is a system that Matt Fitzgerald lays out in his book Racing Weight and encourages you to eat a good range of healthy foods and no unhealthy foods. I intend the app to make tracking this easier.

How will it work?

The app will be a fairly simple thing in the first iteration. I imagine it will have a simple database stored on the phone that will track your daily score across the eleven food groups defined in the DQS system. The main screen will show your current total score for the day and score for each food group:

<mock of home screen>

Clicking one of the food groups will take you to a details screen where you can add or subtract a serving of the food type. This screen will also show you the score for the food type, how many servings you've had and the points scale associated with the food type.

<mock of details screen>

Learning WP7

I've messed around with the Windows Phone tools and Silverlight on a number of occasions but never really got my head around XAML and the structure of a Silverlight application because I never had any focus so it was easy to lose direction and give up. With all these things tying together I intend to learn WP7 and publish an app to the app store. I'll be documenting my journey along the way.

Getting a 404 Not Found in the root of Orchard CMS when deployed to your hosting?

 

Over the weekend I’ve been using Orchard CMS to put together a quick website for myself and I ran into a little bump when deploying the application to my server. After deploying the database including creating a new user and updating the settings for the site in the Orchard App_Data directory I was getting a 404 in the root of the application. I was even getting a 404 when I tried to hit a static resource. 

The problem was that the database user was set to change their password at the next login. This was a quick fix but it did take me 10 minutes to figure out while I made sure I’d got the ASP.NET wildcard mapping and other things set up. Moral of the story is, make sure you've had a coffee or two before you deploy your website.

Olympic Tiddlywinks

  • I don't have Adobe Reader installed on my workstation.
  • I don't have local admin rights.
  • There are three admin accounts for the domain we're on. The guy can't remember two of them, the one he can remember is definitely locked out.
  • I'm nearly asleep.

This project is currently in a "gearing up" phase. Does anyone want to start an Olympic Tiddlywinks team?

Becoming A Contractor

The last six months have been quite different. In June I left my longterm position at twentysix to move to eibDigital. This was a move I hoped would broaden my mind but unfortunately it didn't*, so in December I decided to make the jump and become a contractor.

Timing

Like everyone I've whinged about my job on many occasions over the last six years, often suggesting that one day I would go freelance but never actually following through. The biggest thing that helped me make it a reality this time was I knew I'd got some work. A friend had mentioned his business needed an internal system building and we'd begun to spec it out. I also knew that they were willing to pay a realistic amount** for it.

That being said, entering the world of contracting at the beginning of December maybe wasn't the best plan. Once I'd built the main chunk of the afore mentioned system I started looking for my next pay check and found work was sparse around the Christmas period. This seems like common sense, people take holiday and are out of the office so the negotiations with recruiters take longer than normal. In hindsight I would have hung in there for an extra month to get me over the Christmas period and into the new year when all the project managers and technical directors are back in the office full time and maybe have new budgets to spend.

Tip #1: Unless you absolutely have to leave NOW***, wait until the new financial year before becoming a contactor

 


* Maybe my mind is too narow?

** Not the usual "I'll give you 50 quid and this old toaster that stopped working in 1994".

*** Or maybe you've got 30 grand in the bank.

Enum of T - A Generic Utility Class For Working With Enumerations

I can never ever find this class whenever I need it which is quite often so in the interest of findability Logarythmik's Enum<T> class over on his(?) blog. 

On Nonbreaking Spaces

Be careful when copying text from Microsoft Excel into your favourite CMS. I've just spent half an hour trying to figure out why some text wouldn't wrap. My first thought is "it's gotta be non-breaking spaces", so I checked the HTML source and to my surprise there were actual spaces in there, not the little &nbsp; I was expecting.

This can't be right!

and it wasn't. I debugged by page in Visual Studio and at the point where I was getting the string from the CMS and outputting it to the page I got the space character from the string and checked what char it was;

char space = CurrentPage["Foo"].ToString()[8];

The character code was 160, 160 is the decimal character code for a non-breaking space. &nbsp; is the HTML entity that represents this. It'd never occured to me that the non-breaking space was a literal character as well as the HTML entity, but of course it is, because what else would the HTML entity render as.

EPiServer and MvcMiniProfiler - Profiled Base Classes

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.

EPiServer and MvcMiniProfiler - A Profiled DataFactory

Last time we took a quick look into MvcMiniProfiler, how to get it up and running on EPiServer and the basics of profiling code.

This time, we'll take a look at the DataFactory and profiling it's use.

ProfiledDataFactory

A lot of the time, when you're not working with the CurrentPage PageData object, you're working with the EPiServer DataFactory to obtain and manipulate PageData objects. ProfiledDataFactory is a simple wrapper around the default DataFactory; I've implemented all the methods which aren't marked with the Obsolete attribute so the class is a drop-in replacement for EPiServer.DataFactory. As a side note, if EPiServer employed Dependency Injection or was more losely coupled, we might have been able to replace the default DataFactory throughout the framework. (double click the code to select it as plain text)

 

using System.Collections.Generic;
using EPiServer.Configuration;
using EPiServer.Core;
using EPiServer.DataAbstraction;
using EPiServer.DataAccess;
using EPiServer.Security;
using MvcProf = MvcMiniProfiler;

namespace EPiServer.MiniProfiler
{
    public sealed class ProfiledDataFactory
    {
        private static ProfiledDataFactory _instance = new ProfiledDataFactory();

        public static ProfiledDataFactory Instance
        {
            get { return _instance; }
        }

        public PageReference Copy(PageReference pageLink, PageReference destinationLink)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory Copy"))
                return DataFactory.Instance.Copy(pageLink, destinationLink);
        }

        public PageReference Copy(PageReference pageLink, PageReference destinationLink, bool allowThreading)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory Copy"))
                return DataFactory.Instance.Copy(pageLink, destinationLink, allowThreading);
        }

        public PageReference Copy(PageReference pageLink, PageReference destinationLink, bool publishOnDestination, bool allowThreading)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory Copy"))
                return DataFactory.Instance.Copy(pageLink, destinationLink, publishOnDestination, allowThreading);
        }

        public PageReference Copy(PageReference pageLink, PageReference destinationLink, AccessLevel requiredSourceAccess, AccessLevel requiredDestinationAccess, bool publishOnDestination, bool allowThreading)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory Copy"))
                return DataFactory.Instance.Copy(pageLink, destinationLink, requiredSourceAccess, requiredDestinationAccess, publishOnDestination, allowThreading);
        }

        public PageData CreateLanguageBranch(PageReference pageLink, ILanguageSelector selector)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory CreateLanguageBranch"))
                return DataFactory.Instance.CreateLanguageBranch(pageLink, selector);
        }

        public PageData CreateLanguageBranch(PageReference pageLink, ILanguageSelector selector, AccessLevel access)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory CreateLanguageBranch"))
                return DataFactory.Instance.CreateLanguageBranch(pageLink, selector, access);
        }

        public void Delete(PageReference pageLink, bool forceDelete)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory Delete"))
                DataFactory.Instance.Delete(pageLink, forceDelete);
        }

        public void Delete(PageReference pageLink, bool forceDelete, AccessLevel access)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory Delete"))
                DataFactory.Instance.Delete(pageLink, forceDelete, access);
        }

        public void DeleteChildren(PageReference pageLink, bool forceDelete)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory DeleteChildren"))
                DataFactory.Instance.DeleteChildren(pageLink, forceDelete);
        }

        public void DeleteChildren(PageReference pageLink, bool forceDelete, AccessLevel access)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory DeleteChildren"))
                DataFactory.Instance.DeleteChildren(pageLink, forceDelete, access);
        }

        public void DeleteLanguageBranch(PageReference pageLink, string languageBranch)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory DeleteLanguageBranch"))
                DataFactory.Instance.DeleteLanguageBranch(pageLink, languageBranch);
        }

        public void DeleteLanguageBranch(PageReference pageLink, string languageBranch, AccessLevel access)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory DeleteLanguageBranch"))
                DataFactory.Instance.DeleteLanguageBranch(pageLink, languageBranch, access);
        }

        public void DeleteVersion(PageReference pageLink)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory DeleteVersion"))
                DataFactory.Instance.DeleteVersion(pageLink);
        }

        public void DeleteVersion(PageReference pageLink, AccessLevel access)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory DeleteVersion"))
                DataFactory.Instance.DeleteVersion(pageLink, access);
        }

        public PageDataCollection FindAllPagesWithCriteria(PageReference pageLink, PropertyCriteriaCollection criterias, string languageBranch, ILanguageSelector selector)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory FindAllPagesWithCriteria"))
                return DataFactory.Instance.FindAllPagesWithCriteria(pageLink, criterias, languageBranch, selector);
        }

        public PageDataCollection FindPagesWithCriteria(PageReference pageLink, PropertyCriteriaCollection criterias)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory FindPagesWithCriteria"))
                return DataFactory.Instance.FindPagesWithCriteria(pageLink, criterias);
        }

        public PageDataCollection FindPagesWithCriteria(PageReference pageLink, PropertyCriteriaCollection criterias, string languageBranch)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory FindPagesWithCriteria"))
                return DataFactory.Instance.FindPagesWithCriteria(pageLink, criterias, languageBranch);
        }

        public PageDataCollection FindPagesWithCriteria(PageReference pageLink, PropertyCriteriaCollection criterias, string languageBranch, ILanguageSelector selector)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory FindPagesWithCriteria"))
                return DataFactory.Instance.FindPagesWithCriteria(pageLink, criterias, languageBranch, selector);
        }

        public PageDataCollection GetChildren(PageReference pageLink)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory GetChildren"))
                return DataFactory.Instance.GetChildren(pageLink);
        }

        public PageDataCollection GetChildren(PageReference pageLink, ILanguageSelector selector)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory GetChildren"))
                return DataFactory.Instance.GetChildren(pageLink, selector);
        }

        public PageDataCollection GetChildren(PageReference pageLink, ILanguageSelector selector, int startIndex, int maxRows)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory GetChildren"))
                return DataFactory.Instance.GetChildren(pageLink, selector, startIndex, maxRows);
        }

        public PageData GetDefaultPageData(PageReference parentPageLink, int pageTypeID)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory GetDefaultPageData"))
                return DataFactory.Instance.GetDefaultPageData(parentPageLink, pageTypeID);
        }

        public PageData GetDefaultPageData(PageReference parentPageLink, string pageTypeName)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory GetDefaultPageData"))
                return DataFactory.Instance.GetDefaultPageData(parentPageLink, pageTypeName);
        }

        public PageData GetDefaultPageData(PageReference parentPageLink, int pageTypeID, ILanguageSelector selector)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory GetDefaultPageData"))
                return DataFactory.Instance.GetDefaultPageData(parentPageLink, pageTypeID, selector);
        }

        public PageData GetDefaultPageData(PageReference parentPageLink, string pageTypeName, ILanguageSelector selector)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory GetDefaultPageData"))
                return DataFactory.Instance.GetDefaultPageData(parentPageLink, pageTypeName, selector);
        }

        public IList<PageReference> GetDescendents(PageReference pageLink)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory GetDescendents"))
                return DataFactory.Instance.GetDescendents(pageLink);
        }

        public PageDataCollection GetLanguageBranches(PageReference pageLink)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory GetLanguageBranches"))
                return DataFactory.Instance.GetLanguageBranches(pageLink);
        }

        public PageReferenceCollection GetLinksToPages(PageReference pageLink)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory GetLinksToPages"))
                return DataFactory.Instance.GetLinksToPages(pageLink);
        }

        public PageData GetPage(PageReference pageLink)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory GetPage"))
                return DataFactory.Instance.GetPage(pageLink);
        }

        public PageData GetPage(PageReference pageLink, ILanguageSelector selector)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory GetPage"))
                return DataFactory.Instance.GetPage(pageLink, selector);
        }

        public PageProviderBase GetPageProvider(PageReference pageLink)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory GetPageProvider"))
                return DataFactory.Instance.GetPageProvider(pageLink);
        }

        public PageProviderBase GetPageProvider(string remoteSiteName)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory GetPageProvider"))
                return DataFactory.Instance.GetPageProvider(remoteSiteName);
        }

        public PageDataCollection GetPages(IList<PageReference> pageLinks, ILanguageSelector selector)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory GetPages"))
                return DataFactory.Instance.GetPages(pageLinks, selector);
        }

        public PagePath GetParents(PageReference pageLink)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory GetParents"))
                return DataFactory.Instance.GetParents(pageLink);
        }

        public Settings GetSettingsFromPage(PageReference pageLink)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory GetSettingsFromPage"))
                return DataFactory.Instance.GetSettingsFromPage(pageLink);
        }

        public Settings GetSettingsFromPage(PageReference pageLink, bool fallbackToStartPageId)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory GetSettingsFromPage"))
                return DataFactory.Instance.GetSettingsFromPage(pageLink, fallbackToStartPageId);
        }

        public bool IsCapabilitySupported(PageReference pageLink, PageProviderCapabilities capability)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory IsCapabilitySupported"))
                return DataFactory.Instance.IsCapabilitySupported(pageLink, capability);
        }

        public bool IsWastebasket(PageReference pageLink)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory IsWastebasket"))
                return DataFactory.Instance.IsWastebasket(pageLink);
        }

        public PageDataCollection ListDelayedPublish()
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory ListDelayedPublish"))
                return DataFactory.Instance.ListDelayedPublish();
        }

        public PageVersionCollection ListPublishedVersions(PageReference pageLink)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory ListPublishedVersions"))
                return DataFactory.Instance.ListPublishedVersions(pageLink);
        }

        public PageVersionCollection ListVersions(PageReference pageLink)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory ListVersions"))
                return DataFactory.Instance.ListVersions(pageLink);
        }

        public PageVersionCollection ListVersions(PageReference pageLink, string languageBranch)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory ListVersions"))
                return DataFactory.Instance.ListVersions(pageLink, languageBranch);
        }

        public PageVersion LoadPublishedVersion(PageReference pageLink)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory LoadPublishedVersion"))
                return DataFactory.Instance.LoadPublishedVersion(pageLink);
        }

        public PageVersion LoadPublishedVersion(PageReference pageLink, string languageBranch)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory LoadPublishedVersion"))
                return DataFactory.Instance.LoadPublishedVersion(pageLink, languageBranch);
        }

        public PageVersion LoadVersion(PageReference pageLink)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory LoadVersion"))
                return DataFactory.Instance.LoadVersion(pageLink);
        }

        public void Move(PageReference pageLink, PageReference destinationLink)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory Move"))
                DataFactory.Instance.Move(pageLink, destinationLink);
        }

        public void Move(PageReference pageLink, PageReference destinationLink, AccessLevel requiredSourceAccess, AccessLevel requiredDestinationAccess)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory Move"))
                DataFactory.Instance.Move(pageLink, destinationLink, requiredSourceAccess, requiredDestinationAccess);
        }

        public void MoveToWastebasket(PageReference pageLink)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory MoveToWastebasket"))
                DataFactory.Instance.MoveToWastebasket(pageLink);
        }

        public void ResetCounters()
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory ResetCounters"))
                DataFactory.Instance.ResetCounters();
        }

        public PageReference ResolvePageFolder(int pageFolderId)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory ResolvePageFolder"))
                return DataFactory.Instance.ResolvePageFolder(pageFolderId);
        }

        public PageReference Save(PageData page, SaveAction action)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory Save"))
                return DataFactory.Instance.Save(page, action);
        }

        public PageReference Save(PageData page, SaveAction action, AccessLevel access)
        {
            using (MvcProf.MiniProfiler.StepStatic("ProfiledDataFactory Save"))
                return DataFactory.Instance.Save(page, action, access);
        }
    }
}

All we need to do now is replace and calls to DataFactory.Instance with ProfiledDataFactory.Instance.

 

protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);
    NewsItems.DataSource = ProfiledDataFactory.Instance.GetChildren(CurrentPageLink);
    NewsItems.DataBind();
}

 

This will give us profile information like so:

Profile data showing call to ProfiledDataFactory.Instance.GetChildren

Caveat

A lot of the DataFactory methods such as GetPage are duplicated within the PageBase class. These methods simply wrap calls to DataFactory.Instance.GetPage and the like. If you use these methods, they won't provide any profile information because they're not using the ProfiledDataFactory. As I mentioned earlier, if EPiServer allowed us to replace DataFactory.Instance with anything that implements an IDataFactory interface or an object which inherited from PageStoreBase, we'd be able to plug this ProfiledDataStore in throughout our EPiServer application.

Next Time

In the next post, we'll explore replacing some of the EPiServer base classes for pages and user controls to get some basic profiling infrastructure within your application.