Profiling NHibernate or Linq to SQL in an EPiServer CMS solution

Hibernating Rhinos has two really good products for profiling applications that are using NHibernate or Linq to SQL, NHibernate Profiler and Linq to Sql Profiler. To profile your application you need to reference an appender and initialize it upon startup. This can easily be done in several ways in EPiServer CMS, using HTTP modules, the application start event on HttpApplication or with PlugInAttributes to mention a few. I prefer to create an InitializationModule in a separate assembly, which gives you the ability to add and remove profiling by adding/removing the assembly to/form the Bin folder. This way, you don’t have to release a new version of your code to enable profiling and it’s easy to reuse in all your solutions.

[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class NHibernateProfilerInitializationModule : IInitializableModule
{
	#region IInitializableModule Members

	public void Initialize(EPiServer.Framework.Initialization.InitializationEngine context)
	{
		NHibernateProfiler.Initialize();
	}

	public void Preload(string[] parameters)
	{
	}

	public void Uninitialize(EPiServer.Framework.Initialization.InitializationEngine context)
	{
	}

	#endregion
}
[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class LinqToSqlProfilerInitializationModule : IInitializableModule
{
	#region IInitializableModule Members

	public void Initialize(EPiServer.Framework.Initialization.InitializationEngine context)
	{
		LinqToSqlProfiler.Initialize();
	}

	public void Preload(string[] parameters)
	{
	}

	public void Uninitialize(EPiServer.Framework.Initialization.InitializationEngine context)
	{
	}

	#endregion
}

Analyze EPiServer (log4net) logs with SQL Server

In my previous post I described how to query an IIS log as if it was a database table using SQL Servers OPENROWSET. This post is a short follow up on that describing how to query EPiServer (log4net) logs in the same way. In EPiServers logfiles, there are no character sequence that can be used as row delimiter, which means we need to preprocess the file in some way and inject a good row delimiter. I’ve been using RxFind to inject <logdelim/> after each log entry in the file.

rxfind mylogfile.txt /p:"\n([0-9]{4})-" /r:"<logdelim/>${1}-"

Then you can use a format file like this one:

<?xml version="1.0"?>
<BCPFORMAT xmlns="http://schemas.microsoft.com/sqlserver/2004/bulkload/format" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <RECORD>
        <FIELD ID="1" xsi:type="CharTerm" TERMINATOR=" " MAX_LENGTH="10"/>
        <FIELD ID="2" xsi:type="CharTerm" TERMINATOR=" " MAX_LENGTH="15"/>
        <FIELD ID="3" xsi:type="CharTerm" TERMINATOR=" [" MAX_LENGTH="20"/>
        <FIELD ID="4" xsi:type="CharTerm" TERMINATOR="] " MAX_LENGTH="20"/>
        <FIELD ID="5" xsi:type="CharTerm" TERMINATOR="\r\n" MAX_LENGTH="20000"/>
        <FIELD ID="6" xsi:type="CharTerm" TERMINATOR="&lt;logdelim/&gt;" MAX_LENGTH="20000"/>
    </RECORD>
    <ROW>
        <COLUMN SOURCE="1" NAME="date" xsi:type="SQLNVARCHAR"/>
        <COLUMN SOURCE="2" NAME="time" xsi:type="SQLNVARCHAR"/>
        <COLUMN SOURCE="3" NAME="type" xsi:type="SQLNVARCHAR"/>
        <COLUMN SOURCE="4" NAME="thread" xsi:type="SQLINT"/>
        <COLUMN SOURCE="5" NAME="description" xsi:type="SQLNVARCHAR" LENGTH="MAX"/>
        <COLUMN SOURCE="6" NAME="text" xsi:type="SQLNVARCHAR" LENGTH="MAX"/>
    </ROW>
</BCPFORMAT>

to query your log file like this:

select
    top 25 *
from
    openrowset(
        bulk 'c:\mylogfile.txt',
        formatfile = 'c:\errorlog-format.xml'
    ) as logfile

This post was first published on EPiServer World, available here.

Analyze IIS logs using SQL Server

IIS logs are a good source of information when you want to analyze the behavior of your site. There are many tools and log parsers around, but I tend to prefer SQL Servers OPENROWSET to parse and query log files.

To use OPENROWSET you need to map the format of the file to a virtual database table using an Xml Format file. I use a file like this one:

<?xml version="1.0"?>
<BCPFORMAT xmlns="http://schemas.microsoft.com/sqlserver/2004/bulkload/format" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <RECORD>
        <FIELD ID="1" xsi:type="CharTerm" TERMINATOR=", " MAX_LENGTH="15"/>
        <FIELD ID="2" xsi:type="CharTerm" TERMINATOR=", " MAX_LENGTH="20"/>
        <FIELD ID="3" xsi:type="CharTerm" TERMINATOR=", " MAX_LENGTH="20"/>
        <FIELD ID="4" xsi:type="CharTerm" TERMINATOR=", " MAX_LENGTH="20"/>
        <FIELD ID="5" xsi:type="CharTerm" TERMINATOR=", " MAX_LENGTH="20"/>
        <FIELD ID="6" xsi:type="CharTerm" TERMINATOR=", " MAX_LENGTH="20"/>
        <FIELD ID="7" xsi:type="CharTerm" TERMINATOR=", " MAX_LENGTH="20"/>
        <FIELD ID="8" xsi:type="CharTerm" TERMINATOR=", " MAX_LENGTH="20"/>
        <FIELD ID="9" xsi:type="CharTerm" TERMINATOR=", " MAX_LENGTH="20"/>
        <FIELD ID="10" xsi:type="CharTerm" TERMINATOR=", " MAX_LENGTH="20"/>
        <FIELD ID="11" xsi:type="CharTerm" TERMINATOR=", " MAX_LENGTH="20"/>
        <FIELD ID="12" xsi:type="CharTerm" TERMINATOR=", " MAX_LENGTH="20"/>
        <FIELD ID="13" xsi:type="CharTerm" TERMINATOR=", " MAX_LENGTH="20"/>
        <FIELD ID="14" xsi:type="CharTerm" TERMINATOR=", " MAX_LENGTH="2000"/>
        <FIELD ID="15" xsi:type="CharTerm" TERMINATOR=",\r\n" MAX_LENGTH="2000"/>
    </RECORD>
    <ROW>
        <COLUMN SOURCE="1" NAME="clientip" xsi:type="SQLNVARCHAR"/>
        <COLUMN SOURCE="2" NAME="username" xsi:type="SQLNVARCHAR"/>
        <COLUMN SOURCE="3" NAME="date" xsi:type="SQLNVARCHAR"/>
        <COLUMN SOURCE="4" NAME="time" xsi:type="SQLNVARCHAR"/>
        <COLUMN SOURCE="5" NAME="sitename" xsi:type="SQLNVARCHAR"/>
        <COLUMN SOURCE="6" NAME="servername" xsi:type="SQLNVARCHAR"/>
        <COLUMN SOURCE="7" NAME="serverip" xsi:type="SQLNVARCHAR"/>
        <COLUMN SOURCE="8" NAME="timetaken" xsi:type="SQLINT"/>
        <COLUMN SOURCE="9" NAME="clientbytes" xsi:type="SQLINT"/>
        <COLUMN SOURCE="10" NAME="serverbytes" xsi:type="SQLINT"/>
        <COLUMN SOURCE="11" NAME="httpstatus" xsi:type="SQLINT"/>
        <COLUMN SOURCE="12" NAME="windowsstatus" xsi:type="SQLINT"/>
        <COLUMN SOURCE="13" NAME="httpmethod" xsi:type="SQLNVARCHAR"/>
        <COLUMN SOURCE="14" NAME="url" xsi:type="SQLNVARCHAR"/>
        <COLUMN SOURCE="15" NAME="params" xsi:type="SQLNVARCHAR"/>
    </ROW>
</BCPFORMAT>

With this format file in place, it’s easy to query a log file using standard SQL statements like this one:

select top 25 *
from
    openrowset(
        bulk 'c:\mylogfile.log',
        formatfile = 'c:\iislog-format.xml'
    ) as logfile

I have written some useful queries that are available in the Code section along with the format file. These are:

  • min and max date in log file
  • requests ordered by time
  • requests grouped by http-status
  • requests grouped by windows-status
  • requests grouped by http-method
  • requests grouped by username
  • requests grouped by clientip
  • requests grouped by file extension
  • requests grouped by minute
  • 25 most requested urls (without querystring)
  • 25 most requested urls (with querystring)
  • 25 slowest requested urls
  • 25 slowest requested urls (in average)
  • requested urls with file extension ordered by time

This post was first published on EPiServer World, available here.

Hidden feature: Generate demo license in admin mode

EPiServer CMS 6 introduced new tools for site and license administration. One not so known feature in these tools are the ability to generate demo licenses. In admin mode, click Site Information under the Config tab. Select the License Information tab and scroll down to Download License section. Select Demo license and hit Download License. The license will be generated, downloaded and installed on your site automatically.

This post was first published on EPiServer World, available here.

Enable drag and drop of PageReferences in edit mode

In my previous post I described how the number of clicks within edit mode can be reduced by adding keyboard shortcuts for common tasks like switching between tabs and saving pages. Another very common and click intensive task in EPiServer CMS is to set the value of PageReference properties. First the editor has to click the browse button on the edit control to bring up the Select Page dialog. Then the editor needs to navigate to the right page, select it and click the Select button. All these tasks involves clicking and is impossible (or at least hard) to do using only keyboard shortcuts. A better concept for setting page references would be to enable drag and drop from the edit tree to the right property control in edit panel.

When I started to look in to this I realized that enabling drag and drop from the standard page structure would be hard, because EPiServer CMS 6 uses drag and drop for reordering pages. I’m not saying it’s not doable, but it involves some tricky parts. So I started out creating a custom edit tree plug-in that lists some pages. In my example code, only the children of the start page are listed, but it’s quite easy to customize this plug-in if you need to.

First I tried to write all the JavaScript on my own, but it turned out to be really hard to support all the common browsers. I asked Ted Nyberg at Ted & Gustaf for some advice and he hinted me to look into the native drag and drop support in HTML5, which lead me to a great article by Zoltan Hawryluk on Cross browser HTML5 drag and drop. This article provides some helper classes to enable drag and drop which I use.

The implementation is quite easy. I have implemented a ControlAdapter where I add a CSS class to the InputPageReference property and register some JavaScript includes. The same JavaScript includes are added to the page tree plug-in (Pages.ascx). In InputPageReference.js I register events for dragstart, dragover, dragleave and drop on all textboxes and all InputPageReference controls. On the drop event I check if the data being dragged is of type pagereference and if the drop target is of type InputPageReference. If both are true, the control is updated with the correct values, otherwise I just fall back to the default browser behavior. The fallback is important to support drag and drop from the file manager.

All my example code is available in the Code section.

This post was first published on EPiServer World, available here.

Add support for keyboard shortcuts in edit mode

One thing editors sometimes ask me is if it’s possible to reduce the number of mouse clicks within the EPiServer edit mode. We all know that creating even the simplest page requires a lot of clicks to get the page created and saved. One way to solve this could be the use of keyboard shortcuts. And it turns out to be quite easy to implement. I just uploaded some example code to the Code section (found here). The example contains of a plug-in and two JavaScript files. The plug-in will register the two JavaScript files each time the edit panel is loaded. Shortcuts.js is an open source library to register, listen and act on keyboard shortcuts. Editpanel.js is a jQuery script where we can bind up our shortcuts. In my example file, I have added shortcuts for the tabs in edit panel and to the SaveSave and view and Save and publish buttons. Feel free to add your own shortcuts.

Shortcut Description
Ctrl+1 Selects the first tab in edit panel (usually Preview)
Ctrl+2 Selects the second tab in edit panel (usually Edit)
Ctrl+Shift+1 Selects the first tab in sub tabs (usually Content)
Ctrl+Shift+2 Selects the second tab in sub tabs
Ctrl+S Save
Ctrl+Shift+V Save and view
Ctrl+Shift+P Save and publish

This post was first published on EPiServer World, available here.

Create and consume a ScriptService the easy way

Nowadays almost all EPiServer projects involves some kind of interactivity powered by jQuery and AJAX. When it come to frameworks, there are a lot of alternatives to choose from on both the client- and serverside. On the clientside, jQuery has emerged to be the alternative to use. On the serverside, Microsoft offers a couple of different alternatives and at a first glance one might think WCF is the way go. Personally I think WCF might be great for big distributed solutions but could be a bit to complex for just feeding jQuery with some simple data. Another strategy I’ve seen quite often is concatenation of strings to create simple JSON objects. Simple, but maybe not that good.

The most recent versions of EPiServer uses Microsoft .NET Framework 3.5 which includes some great stuff for writing services for scripting.

In this post I will add a simple service to the EPiServer Public Templates which will contain two methods, one for fetching a list of objects and one that accept an object as parameter and returns a string.

First we need to create a model object that we can serialize to JSON and send to the browser. Try to keep the model object as small as possible i.e. do not return the entire PageData object. Lets create a Person class.

public class Person
{
    public string Firstname { get; set; }
    public string Lastname { get; set; }
}

Next, create a new folder called Services under Templates/Public. Within the new folder, create a Web Service and name it to PersonService.asmx.

Uncomment the ScriptService attribute on the class, set a meaningful namespace and remove the HelloWorld method. We will add two methods and end up in a service class like this one:

[WebService(Namespace = "http://local.lab.com/services/personservice/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService]
public class PersonService : WebService
{
    [WebMethod]
    [ScriptMethod(ResponseFormat = ResponseFormat.Json)]
    public List<Person> GetPersons()
    {
        return new List<Person>
            {
                new Person { Firstname = "Foo", Lastname = "Bar" },
                new Person { Firstname = "Bar", Lastname = "Foo" }
            };
    }

    [WebMethod]
    [ScriptMethod(ResponseFormat = ResponseFormat.Json)]
    public string GetFullname(Person person)
    {
        return string.Format("{0} {1}",
            person.Firstname, person.Lastname);
    }
}

With a default installation of EPiServer, web.config does not use the scripting goodies in .NET Framework 3.5. To use this for our services add the following configuration for our Services folder:

<location path="Templates/Public/Services">
    <system.web>
        <webServices>
            <protocols>
                <add name="HttpPost" />
            </protocols>
        </webServices>
    </system.web>
    <system.webServer>
        <handlers>
            <clear />
                <add name="ScriptHandlerFactory"
                     verb="*"
                     path="*"
                     perCondition="integratedMode"
                     type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        </handlers>
    </system.webServer>
</location>

Next, we need to add some jQuery code to consume our service. Add main.js to our project and make sure you include it in the header tag together with jQuery itself. Add the classic jQuery stub to main.js:

$(document).ready(function() {

});

Within the stub, add a handler for GetPersons

$("#GetPersons").click(function(event) {
    $.ajax({
        type: "POST",
        url: "/Templates/Public/Services/PersonService.asmx/GetPersons",
        cache: false,
        data: "{}",
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        error: function(e) {
            alert("Error: " + e.responseText);
        },
        success: function(result) {
            var data = (typeof result.d == 'string' ? eval('(' + result.d + ')') : result.d);
            var listHtml = '<ul>';
            $.each(data, function() {
                listHtml += '<li>' + this.Firstname + ' ' + this.Lastname + '</li>';
            });
            listHtml += '</ul>';
            $("#result").empty().append(listHtml);
        }
    });
});

and a handler for GetFullname

$("#GetFullname").click(function(event) {
    $.ajax({
        type: "POST",
        url: "/Templates/Public/Services/PersonService.asmx/GetFullname",
        cache: false,
        data: "{'person':{'Firstname':'Marthin','Lastname':'Freij'}}",
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        error: function(e) {
            alert("Error: " + e.responseText);
        },
        success: function(result) {
            $("#result").empty().append(result.d);
        }
    });
});

Next we need to add some html that jQuery can hook in to

<a href="#" id="GetPersons">GetPersons</a> |
<a href="#" id="GetFullname">GetFullanme</a>
<hr />
<div id="result"></div>

Ok, that should make it. Let’s test it by clicking the links

Great, it should work! Due to the hands-on nature of this post, some details are not covered. Don’t hesitate to ask if you want some details more explained.

This post was first published on EPiServer World, available here.

Find the most recently changed page

In my current project I need to know when the latest change was made on any page on the entire site. My first thought was to use FindPagesWithCriteria or maybe to do a recursive GetChildren to find the latest value of the PageData.Changed property. But since EPiServer CMS 6, there is a change log you can ask for this kind of stuff. There is even a public class, used by the Recently Changed Pages gadget you can use, RecentlyChangedPagesFinder.

public DateTime LastModified
{
    get
    {
        RecentlyChangePage lastChangedPage =
            new RecentlyChangedPageFinder().Find(1).FirstOrDefault();

        if (lastChangedPage != null)
            return lastChangedPage.Changed;

        return DateTime.MinValue;
    }
}

This post was first published on EPiServer World, available here.

EPiServer – ReflectionTypeLoadException: Unable to load one or more of the requested types

Today I installed Page Type Builder 1.2 Beta 2 on my ongoing EPiServer CMS 6 project. I dropped the Page Type Builder assemblies in the bin folder, recreated the references, did an iisreset and then tried to start the site. I got the following exception:

Unable to load one or more of the requested types. The following information may be a subset of the Type/LoaderException information present – inspect with debugger for complete view.
Check assemblies [PageTypeBuilder, Version=1.1.9.1, Culture=neutral, PublicKeyToken=6fb8762af0e6dbed] and/or types [PageTypeBuilder.Reflection.MethodInfoExtensions,
PageTypeBuilder.Abstractions.PageTypeFactory,
PageTypeBuilder.Activation.PageTypePropertyInterceptor,
PageTypeBuilder.Discovery.PageTypeDefinition,
PageTypeBuilder.Synchronization.PageTypePropertyUpdater,
PageTypeBuilder.Discovery.PageTypePropertyDefinitionLocator,
PageTypeBuilder.Initializer,
PageTypeBuilder.Synchronization.PageTypeUpdater,
PageTypeBuilder.PageTypeBuilderException,
PageTypeBuilder.UI.PageDataValidationExtensions,
PageTypeBuilder.Synchronization.Validation.UnmappablePropertyTypeException,
PageTypeBuilder.Discovery.PageTypeDefinitionLocator,
PageTypeBuilder.PageDataExtensionMethods,
PageTypeBuilder.Activation.PageTypePropertiesProxyGenerationHook,
PageTypeBuilder.Synchronization.TabDefinitionUpdater,
PageTypeBuilder.Reflection.AttributedTypesUtility,
PageTypeBuilder.Discovery.PageTypePropertyDefinition,
PageTypeBuilder.Synchronization.TabDefinitionExtensions,
PageTypeBuilder.Synchronization.PageTypeSynchronizer,
PageTypeBuilder.Synchronization.PageTypePropertyDefinitionExtensions]. Information from LoaderExceptions property [Request for the permission of type 'System.Web.AspNetHostingPermission, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.,
Request for the permission of type 'System.Web.AspNetHostingPermission, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.,
Request for the permission of type 'System.Web.AspNetHostingPermission, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.,
Request for the permission of type 'System.Web.AspNetHostingPermission, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.,
Request for the permission of type 'System.Web.AspNetHostingPermission, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.].

The reason for this exception is Windows 7 security features. When you download assemblies from Internet they might be marked as unsafe. To solve this, right click each assembly, select Properties and click the Unblock button on the General tab.

This post was first published on EPiServer World, available here.

Debugging tips while using Page Type Builder

Page Type Builder might be one of the best things that happened to EPiServer CMS over the last couple of years. I strongly recommend you to use it. With that said, I want to share a small debugging tip that is very useful while using Page Type Builder. Debugging is in general a lot about inspecting your objects values. Visual Studio offers different approaches for this, like watches, the immediate window or just hovering your objects and inspect the tooltip to mention a few. I usually prefer the last one. It is quick and easy but you often ends up in a lot of scrolling and traversing through deep object graphs. This tip is as simple as showing you how to customize the tooltip to display the most relevant values for you immediately.

Back to Page Type Builder, if you hover a page type class, all you get is the name of the anonymous type generated behind the scenes. Not so useful, which means you need to expand and scroll in the tooltip to find the PageLink for instance.

When working with Page Type Builder it’s recommended that you create a base class that all your page type classes inherit from. This class can be decorated with attributes which applies to all your page type classes. In this example I will use the DebuggerDisplayAttribute (see Using DebuggerDisplay Attribute for more info).

[DebuggerDisplay("PageName: {PageName} PageLink: {PageLink}")]
public class BasePageData : TypedPageData
{

}

This will result in the following tooltip

This post was first published on EPiServer World, available here.