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

by Greg 8. May 2012 08:49

 

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.

Tags: , ,

ASP.NET

Olympic Tiddlywinks

by Greg 15. February 2012 14:21
  • 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?

Tags:

Contracting

Becoming A Contractor

by Greg 30. January 2012 15:11

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.

Tags:

Career | Contracting

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

by Greg 14. December 2011 16:00

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. 

Tags: ,

On Nonbreaking Spaces

by Greg 27. September 2011 10:48

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.

Tags: ,

ASP.NET

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

EPiServer and MvcMiniProfiler - A Profiled DataFactory

by Greg 9. August 2011 12:00

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.

Tags: , , ,

ASP.NET | EPiServer

EPiServer and MvcMiniProfiler - A first look

by Greg 26. July 2011 19:07

It wasn't long after I started using EPiServer that I notices Page Output for ASP.NET Trace doesn't work with the CMS (I even took the time to file a bug with EPiServer but I can't find it now to link to :( ). After some messing around I discovered the only way to show Trace page output is by ending the response prematurely, for example sticking a Response.End() in the PreRender, but that's not a good idea, right... The other alternative is to use trace.axd which isn't a disaster but it's not entirely convenient.

MvcMiniProfiler is a new tool by the good folks over at Stack Exchange which gives a really easy and clean API for tracing within your page requests, it works for the main request and any AJAX calls. Many EPiServer projects require some bespoke data access beyond that usual EPiServer Pages and Properties model, to this end, MiniProfiler has a custom SqlConnection which provides profiling info for your queries that can be used with all of your favourite data access technologies.

In this post, I'm going to walk you through the basics of getting MvcMiniProfiler up and running with EPiServer.

NuGet

First of all, you'll need to install NuGet from NuGet.org or the Visual Studio Extension Manager. We'll need this for two things, installing MvcMiniProfiler but before that we'll need to upgrade our EPiServer project to .NET 4.0.

Installing NuGet from the Visual Studio Extension Manager

You could upgrade from .NET 3.5 to 4.0 manually but luckily for you, there's a package on the EPiServer NuGet gallery that will do it for you in a couple of seconds. Once you've got NuGet installed, edit the Package Manager Settings and add the EPiServer feed (http://nuget.episerver.com/feed/packages.svc) as a source.

Adding the EPiServer Package Source to NuGet

Next select All in the Package Source menu Select all in the package Manager source menu then type the following at the Package Manager prompt:

PM> Install-Package EPiServerCMS6ToNetFour

Hit return and a couple of seconds later, your app will be upgraded to .NET 4.

Now that we're running on the right platform, we can install MvcMiniProfiler. This package is on the official NuGet feed (but if your Package Source is still set to All you're golden) so install it with the following:

PM> Install-Package MiniProfiler

Basic Setup

Now that NuGet has taken care of downloading the required DLLs and added a reference to your project we're ready to start profiling. Note that the new DLLs are in a packages folder in the directory above your web application. There is also a packages.config in the root of your application; you might want to add these to source control. 

Getting MvcMiniProfiler up and running is really easy, all we need to do is four simple things; first, start the profiler when a request starts, then stop it when a request ends. The Application class (Global.asax) has the perfect events for this. I personally like to add a CodeBehind to my Global.asax, so here's my code:

Global.asax

<%@ Application Language="C#" Inherits="EPiServer_MiniProfiler.Global" 
            CodeBehind="Global.asax.cs" %>

Global.asax.cs

using System;
using System.Web;
using MvcMiniProfiler;

namespace EPiServer_MiniProfiler
{
    public class Global : EPiServer.Global
    {
        void Application_BeginRequest(object sender, EventArgs e)
        {
            MiniProfiler.Start();
        }

        void Application_EndRequest(object sender, EventArgs e)
        {
            MiniProfiler.Stop();
        }
    }
}

Thirdly, we need to profile some code. This is done in a really clever way, making use of IDisposable and the using statement. Create a new EPiServer template and in the OnLoad event, add the following code:

protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);
    using (MiniProfiler.StepStatic("Some long running step"))
    {
        // Do some work here
        Thread.Sleep(123);
    }
}

Last but not least we need to render the generated JavaScript to the page. This is made super easy, simply add the following to the <head> of your page:

<%= MvcMiniProfiler.MiniProfiler.RenderIncludes().ToHtmlString() %>

Usage

Now build your project and visit your template. In the top left hand corner you should see a little tab like this:

The MvcMiniProfiler tab

and clicking the tab will show the non-trivial events that were profiled, like so:

MiniProfiler showing all non-trivial events.

AJAX Tracing

As I mentioned earlier, AJAX requests are profiled as well, to demonstrate this lets add a WebMethod to our template and a little bit of JavaScript to call it.

I've added the following WebMethod to the CodeBehind of my template, which incidentally is Default.aspx in the root of the application.

[WebMethod]
public static string Hello()
{
    using (MvcMiniProfiler.MiniProfiler.StepStatic("WebMethod Hello"))
    {
        Thread.Sleep(321);
        return DataFactory.Instance.GetPage(PageReference.StartPage).PageName;
    }
}

Then we need to call this from our browser, to do this I'm using jQuery (from the Google CDN) and a simple ajax call. Add the following to the <head> of your template:

<script type="text/javascript"
          src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
<script type="text/javascript">
    $(function () {
        $.ajax({
            type: "POST",
            url: "Default.aspx/Hello",
            data: "{}",
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            success: function (msg) {
                // Replace the div's content with the page method's return value.
                $("#Result").text("AJAX: " + msg.d);
            }
        });
    });
</script>

and then add a div to the <body> of the page to receive the result of the AJAX call:

<div id="Result">
</div>

Build the project and refresh the page and you should see an extra trace notification stack up in the top left corner like so:

MvcMiniProfiler tab showing AJAX request

You can inspect the profile of this AJAX request by clicking on it just like the main page request.

Next time

We haven't really touched on anything EPiServer specific here but I hope you like what you've seen. MvcMiniProfiler is a quick and easy way to add profiling to your pages and has a really nice API in the form of IDisposable steps which you use to denote sections of your page. 

You can find out more about MvcMiniProfiler on the Google Code page, on Marc Gravell's blog, and on Stack Overflow.

Next time I'm going to introduce a few EPiServer-specific bits and pieces that will help you get from zero to profiled pages much quicker.

Tags: , , , ,

ASP.NET | EPiServer

GetPropertyValue with a Default Value - PageData Extension Methods

by Greg 19. July 2011 11:16

A while ago I blogged some code for a couple of extension methods for the PageData type. An obvious addition that I've been using for a while now but didn't cover in the first post allows you to supply the default value. This works nicely for reference types where default(T) == null and/or where you have a specific default value you want to use.

Reference Type Defaults

For reference types, the default() keyword returns a null reference and as such the previous implementation of GetPropertyValue will return null when the PageData doesn't have the property requested.

Lets say we've got a Post class:

public class Post
{
    public int Id { get; set; }
    public int UserId { get; set; }
    public string Title { get; set; }
    public string Body { get; set; }
}

and we try to retrieve a property of the CurrentPage that doesn't exist:

Post post = CurrentPage.GetPropertyValue<Post>("NonexistentProperty");
if (post == null)
    Debug.WriteLine("default(Post) == null");

This override of GetPropertyValue takes an extra argument of type T which is used as the default return value (instead of default(T)):

/// <summary>
/// Gets the value of a property.
/// </summary>
/// <typeparam name="T">The type to cast the value to.</typeparam>
/// <param name="propertyName">The name of the property to fetch.</param>
/// <param name="defaultValue">The default value.</param>
/// <returns>Returns the value of the property or the suplied defaultValue.</returns>
public static T GetPropertyValue<T>(this PageData pageData, string propertyName, T defaultValue)
{
    if(pageData == null)
        throw new ArgumentNullException("pageData");

    if(string.IsNullOrEmpty(propertyName))
        throw new ArgumentException("You must specify a property name", "propertyName");

    if (pageData.Property[propertyName] != null && 
        pageData.Property[propertyName].Value != null)
    {
        return (T)pageData.Property[propertyName].Value;
    }
    else
    {
        return defaultValue;
    }
}

Value Type Defaults

All value types have a default of zero

For example, the default of the DateTime value type is 01/01/0001 00:00:00 and the default of int is 0.

Using the version of GetPropertyValue that returns default(T) can lead to code like this:

DateTime updated = CurrentPage.GetPropertyValue("LastUpdated");
if (updated == default(DateTime))
    updated = DateTime.Now;

but with this new method we can do the following and save ourselves a little more repetative code:

DateTime updated = 
    CurrentPage.GetPropertyValue<DateTime>("LastUpdated", DateTime.Now);

Tags: , ,

EPiServer

Project Euler - Problem 18, in my head, in 5 minutes

by Greg 20. June 2011 13:50

I was just filling out my About Me page and mentioned Project Euler, which gave me an itch to go and solve a problem, so after a few minutes scratching around, I though't I'd give Problem 18 a pop, and lo and behold, I cracked it in about 5 minutes, in my head.

Next up, I'm going to convert my algorythm into a program and give Problem 67 a go. Problem 67 is the same as 18, except the dataset is 100 rows instead of 10 and has over one trillion possible routes.

Tags: