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

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   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.   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

Adding Custom HTTP Headers to an ASP.NET MVC Response

by Greg 13. December 2010 21:57

Here's a little class I just put together to enable you to add HTTP Headers to a View in ASP.NET MVC 3.

It's pretty simple and I might quite easily have missed something very similar that is built in.

I do however like the idea of using an attribute for this rather than having a bunch of AppendHeader calls in the body of the Controller Action.

/// 
/// Represents an attribute that is used to add HTTP Headers to a Controller Action response.
/// 
public class HttpHeaderAttribute : ActionFilterAttribute
{
    /// 
    /// Gets or sets the name of the HTTP Header.
    /// 
    /// The name.
    public string Name { get; set; }

    /// 
    /// Gets or sets the value of the HTTP Header.
    /// 
    /// The value.
    public string Value { get; set; }

    /// 
    /// Initializes a new instance of the  class.
    /// 
    /// The name.
    /// The value.
    public HttpHeaderAttribute(string name, string value)
    {
        Name = name;
        Value = value;
    }

    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
        filterContext.HttpContext.Response.AppendHeader(Name, Value);
        base.OnResultExecuted(filterContext);
    }
}

You then use it like this:

[HttpHeader("X-Robots-Tag", "noindex")]
public ActionResult Edit(int id)
{
    // ...
}

Tags: ,

ASP.NET

Using Web Deployment Projects in an x86 build configuration

by Greg 3. November 2010 13:57

A while ago I asked a question on Stackoverflow regarding the use of Web Deployment Projects with an x86 build configuration to which I got no answers within a reasonable time frame and I really needed an answer. Fortunately The Gu came to my rescue and put me in touch with a couple of engineers at Microsoft who assisted me with the fix. 

In the true spirit of StackOverflow, I've answered my own question, hopefully with enough information for it to be useful for others.

Tags: , ,

ASP.NET