Improve Sitecore Membership provider performance 2-20 times

The Sitecore Membership provider is built on top of the default .NET membership provider. The .NET membership provider is not known to be the fastest provider available. Sure, if you have a few thousand users in your database it performs well, but if you have, say, 50.000 or even 500.000 users, the provider becomes painfully slow.

500.000 users in Sitecore

500.000 users in Sitecore

But before you start rewriting the whole thing, there is a couple of things you can do to dramatically improve performance.

I would like to thank Ulrich Kronvold, Kristian Magius, Sergey Marchenko, Ivan Sheyenko and everyone that have been involved in getting these SQL scripts. None of the scripts are my own, but I think they are too good to keep for myself.

MODIFY STORED PROCEDURE [dbo].[aspnet_Membership_GetAllUsers] 

This script modifies the [dbo].[aspnet_Membership_GetAllUsers] stored procedure to select values based on indexed fields:



ALTER PROCEDURE [dbo].[aspnet_Membership_GetAllUsers]
    @ApplicationName       nvarchar(256),
    @PageIndex             int,
    @PageSize              int

    DECLARE @ApplicationId uniqueidentifier
    SELECT  @ApplicationId = NULL
    SELECT  @ApplicationId = ApplicationId FROM dbo.aspnet_Applications WHERE LOWER(@ApplicationName) = LoweredApplicationName
    IF (@ApplicationId IS NULL)
        RETURN 0

    -- Set the page bounds
    DECLARE @PageLowerBound int
    DECLARE @PageUpperBound int
    DECLARE @TotalRecords   int
    SET @PageLowerBound = @PageSize * @PageIndex
    SET @PageUpperBound = @PageSize - 1 + @PageLowerBound

    -- Create a temp table TO store the select results
    /* DSZ
    CREATE TABLE #PageIndexForUsers
        IndexId int IDENTITY (0, 1) NOT NULL,
        UserId uniqueidentifier

    -- Insert into our temp table
    INSERT INTO #PageIndexForUsers (UserId)
    SELECT u.UserId
    FROM   dbo.aspnet_Membership m, dbo.aspnet_Users u
    WHERE  u.ApplicationId = @ApplicationId AND u.UserId = m.UserId
    ORDER BY u.UserName

    SELECT @TotalRecords = COUNT(u.UserId)
    FROM dbo.aspnet_Membership m (NOLOCK), dbo.aspnet_Users u (NOLOCK)
    WHERE u.ApplicationId = @ApplicationId AND u.UserId = m.UserId

                        ROW_NUMBER() OVER (ORDER BY u.UserName) AS IndexId,
                FROM dbo.aspnet_Membership m (NOLOCK), dbo.aspnet_Users u (NOLOCK)
                        WHERE u.UserId = m.UserId AND u.ApplicationId = @ApplicationId AND m.ApplicationId = @ApplicationId)
                AS p INNER JOIN dbo.aspnet_Users u ON u.UserName = p.UserName INNER JOIN dbo.aspnet_Membership m ON m.UserId = u.UserId
                IndexId >= @PageLowerBound AND IndexId <= @PageUpperBound
    --ORDER BY UserName
    RETURN @TotalRecords

MODIFY STORED PROCEDURE [dbo].[aspnet_Membership_GetUserByEmail]

This script modifies the stored procedure [dbo].[aspnet_Membership_GetUserByEmail] to select fields based on indexed values:

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[aspnet_Membership_GetUserByEmail]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[aspnet_Membership_GetUserByEmail]



CREATE PROCEDURE [dbo].[aspnet_Membership_GetUserByEmail]
    @ApplicationName  nvarchar(256),
    @Email            nvarchar(256)
    IF( @Email IS NULL )
        SELECT  u.UserName
        FROM    dbo.aspnet_Applications a, dbo.aspnet_Users u, dbo.aspnet_Membership m
        WHERE   LOWER(@ApplicationName) = a.LoweredApplicationName AND
                u.ApplicationId = a.ApplicationId    AND
                u.UserId = m.UserId AND
                m.LoweredEmail IS NULL
        SELECT  u.UserName
        FROM    dbo.aspnet_Applications a, dbo.aspnet_Users u, dbo.aspnet_Membership m
        WHERE   LOWER(@ApplicationName) = a.LoweredApplicationName AND
                u.ApplicationId = a.ApplicationId    AND
                u.UserId = m.UserId AND
                LOWER(@Email) = m.LoweredEmail AND u.ApplicationId = m.ApplicationId

    IF (@@rowcount = 0)

CREATE NEW INDEX [aspnet_Membership_index]

This script creates a clustered index on fields ApplicationID, LoweredEmail and UserID used by the 2 stored procedures above:

IF  EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[aspnet_Membership]') AND name = N'aspnet_Membership_index')
DROP INDEX [aspnet_Membership_index] ON [dbo].[aspnet_Membership] WITH ( ONLINE = OFF )

CREATE CLUSTERED INDEX [aspnet_Membership_index] ON [dbo].[aspnet_Membership] 
	[ApplicationId] ASC,
	[LoweredEmail] ASC,
	[UserId] ASC

CREATE NEW INDEX [aspnet_Users_Index]

This script creates a new clustered index on fields ApplicationID, LoweredUserName and UserID used by the 2 stored procedures above:

IF  EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[aspnet_Users]') AND name = N'aspnet_Users_Index')
DROP INDEX [aspnet_Users_Index] ON [dbo].[aspnet_Users] WITH ( ONLINE = OFF )

CREATE UNIQUE CLUSTERED INDEX [aspnet_Users_Index] ON [dbo].[aspnet_Users] 
	[ApplicationId] ASC,
	[LoweredUserName] ASC,
	[UserId] ASC

With the changes above I managed to get at least twice the performance. Before the changes, the user manager took about 20 seconds to go to next page. After the change, it took only 2 seconds AND I could start using the search field again.

In general the performance gain was 2-5 times. The scripts does not seem to impact the stability of Sitecore, but use them with caution, as they are not official, not supported by me or Sitecore.


These scripts failed in an installation where I used the “switching” provider as my membership provider. Once I reverted to the default “sitecore” provider, the scripts worked. So be careful if you are using a switching provider, and remember to backup the database, or at least the original indexes and stored procedures.

Posted in Sitecore, Sitecore 6, Sitecore 7 | Tagged , , , | 6 Comments

Add text to asp:ValidationSummary (C#)

This trick is especially useful when you have custom code to be executed after your form have been submitted, and still wishes to communicate an error the same way as you communicate form validation errors.

Imagine the following ValidationSummary:

<asp:ValidationSummary ID="valSummary" 

Any form error is displayed in this summary. When the user clicks your submit button you process the form:

protected void btnSubmit_Click(object sender, EventArgs e)
  if (Page.IsValid)
    // do custom processing of form

But is the processing fails, is it too late to pop the validation summary? Not at all.
To do so, add a CustomValidator to your form:


And simply set the error message in the CustomValidator, inside your btnSubmit_Click:

protected void btnSubmit_Click(object sender, EventArgs e)
  if (Page.IsValid)
    bool isOK = ProcessTheFormData();
    if (!isOK)
      // Optional: Update the summary with a nice header
      valSummary.HeaderText = "Oops something went wrong";

      // Mandatory: Invalidate the custom validator, and set a error message
      valCustom.IsValid = false;
      valCustom.ErrorMessage = "A clever error message";




Posted in c#, General .NET | Tagged , , , | Leave a comment

Disable Sitecore SPEAK dialogs

The new Sitecore SPEAK dialogs introduced with Sitecore 7.x are known to cause difficulties for some. Some of the issues are technical or configuration related, others are related to the fact that many users don’t like their cheese to be moved.

Sitecore SPEAK adds these dialogs by overriding the existing Sitecore dialogs. To revert them, simply disable the override.

Go to the \App_Config\Include\Sitecore.Speak.config file and you can see the overrides at the top of the file:

<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:patch="">
      <override xmlControl="Sitecore.Shell.Applications.Media.MediaBrowser" with="/sitecore/client/applications/Dialogs/SelectMediaDialog" />

      <override dialogUrl="/sitecore/shell/Applications/Dialogs/Internal%20link.aspx" with="/sitecore/client/applications/dialogs/InsertLinkViaTreeDialog" />
      <override dialogUrl="/sitecore/shell/Applications/Dialogs/Mail%20link.aspx" with="/sitecore/client/applications/dialogs/InsertEmailDialog" />
      <override dialogUrl="/sitecore/shell/Applications/Dialogs/Anchor%20link.aspx" with="/sitecore/client/applications/dialogs/InsertAnchorDialog" />
      <override dialogUrl="/sitecore/shell/Applications/Item%20browser.aspx" with="/sitecore/client/applications/dialogs/InsertSitecoreItemViaTreeDialog" />


Comment out the SPEAK dialogs you wish to disable, or comment out all of them if you wish to have all SPEAK dialogs to disappear.


Posted in Sitecore 7 | Tagged , | 3 Comments

Sitecore.Web.Authentication.c__DisplayClass5.b__2(Ticket ticket) +52

When logging into the Sitecore client you can run into this exception:

[NullReferenceException: Object reference not set to an instance of an object.]
Sitecore.Web.Authentication.<>c__DisplayClass5.<GetExistingTicket>b__2(Ticket ticket) +52
System.Linq.Enumerable.FirstOrDefault(IEnumerable`1 source, Func`2 predicate) +168
Sitecore.Web.Authentication.TicketManager.CreateTicket(String userName, String startUrl, Boolean persist) +75
Sitecore.Pipelines.LoggedIn.Ticket.Process(LoggedInArgs args) +58

Or this exception:

[ArgumentException: Empty strings are not allowed.
Parameter name: value]
Sitecore.Web.Authentication.Ticket.set_StartUrl(String value) +255
Sitecore.Web.Authentication.Ticket.Parse(String ticket) +349
Sitecore.Web.Authentication.TicketManager.GetTickets() +319
Sitecore.Web.Authentication.TicketManager.CreateTicket(String userName, String startUrl, Boolean persist) +62
Sitecore.Pipelines.LoggedIn.Ticket.Process(LoggedInArgs args) +59

Both exception cast from the Sitecore login dialog:

[TargetInvocationException: Exception has been thrown by the target of an invocation.]
System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor) +0
System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments) +76
System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) +193
System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters) +35
Sitecore.Nexus.Pipelines.NexusPipelineApi.Resume(PipelineArgs args, Pipeline pipeline) +398
Sitecore.Pipelines.Pipeline.Start(PipelineArgs args, Boolean atomic) +327
Sitecore.Pipelines.Pipeline.Start(String pipelineName, PipelineArgs args, Boolean atomic) +197
Sitecore.sitecore.login.LoginPage.Login_LoggedIn(Object sender, EventArgs e) +330
System.Web.UI.WebControls.Login.AttemptLogin() +289
System.Web.UI.WebControls.Login.OnBubbleEvent(Object source, EventArgs e) +105
System.Web.UI.Control.RaiseBubbleEvent(Object source, EventArgs args) +84
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +3804

The reason is that the Ticket property in the CORE database of your solution have gone pair-shaped:

Properties table from CORE database

Properties table from CORE database

This can happen if you:

  • Run update scripts (it happened for me when updating from Sitecore 7.1 to 7.2)
  • Have 2 Sitecore installations accessing the same database in:
    • Different .NET versions
    • Different Sitecore versions

To solve it, delete any SC_TICKET properties from the CORE database:

delete from [YourSitecore_Core].[dbo].[Properties]
where [key] = 'SC_TICKET';

Then restart your .NET application, and you should be fine again.


Posted in Sitecore 7 | Tagged , | 5 Comments

Sitecore users custom profile properties

Sitecore is using the standard .net security framework. This makes it easy to setup custom profiles on users, and to add custom profile properties. To do so, do the following:


Go to the CORE database and create a new template containing the fields you wish to add to a profile. I have added mine at /sitecore/templates/System/Security/CustomProfile but you can put yours anywhere.

Security Template

Security Template


Go to the CORE database and add a new item based on the template you created, below /sitecore/system/Settings/Security/Profiles.

Custom Profile

Custom Profile

Add the new item below the standard “User” profile. Sitecore will automatically base any new user on the first profile in the list.


In this example I have created an extranet user which profile needs to be based on my new profile. To apply the profile to my user I need to write the following code:

public void UpdateUserProfile(string userName, string profileID)
  Sitecore.Security.Accounts.User user = Sitecore.Security.Accounts.User.FromName(userName, true);
  user.Profile.ProfileItemId = profileID;

// How to use the method
UpdateUserProfile("extranet\\bp", "{7BA5AA76-582D-4463-BCF3-775508C8624E}"); 

Remember to prefix the user name with the domain name (in this example, bp is a member of the extranet domain). The GUID is the ID of the profile item I created.

The profile is now based on this profile:

Profile is added to the user

Profile is added to the user


To write text on the custom profile, do the following:

public void UpdateUser(string userName, string address, string phone)
  Sitecore.Security.Accounts.User user = Sitecore.Security.Accounts.User.FromName(userName, true);
  user.Profile.SetCustomProperty("Address", address);
  user.Profile.SetCustomProperty("Phone", phone);

// Calling the method:
UpdateUser("extranet\\bp", "Zepperlinerhallen, Islands Brygge 55, 2300 København S", "70 23 33 30");

The text is now added to the profile:

Text is added to the profile

Text is added to the profile


  • You don’t really need to create the profile in Sitecore. This is just so you can see the custom properties in the Sitecore security editor.
  • The Boolean “true” in the call to Sitecore.Security.Accounts.User.FromName(userName, true) authenticates the user, hence allowing you to edit the user and the user profile.
  • If you get the users (members) with System.Web.Security.Membership.GetAllUsers instead, the user (member) will not be authenticated, and you cannot edit the user (member, sigh…)
  • Remember to call Profile.Save(), if you don’t, the profile is not changed.
Posted in c#, Sitecore 6, Sitecore 7 | Tagged , , , , , | 5 Comments

URL Rewrite and Sitecore

The Microsoft URL Rewrite module is an ISS extension that allows you to rewrite one URL to another using regular expressions. The extension is like the Sitecore aliases on steroids, and is especially useful for mapping old dynamic URL’s to new Sitecore URL’s.
If you launch a new website on an existing domain, you can be forced to keep old API URL’s as they were, and the URL Rewrite will help you with that.

Download the URL Rewrite extension here.

Here is an example on how to use URL Rewrite Rules to map an old API URL to a new Sitecore .ashx CustomHandler.

The URL rewrite rules are easiest kept in a separate configuration file, so you need to apply the following to the system.webserver part of your web.config:

    <rules configSource="App_Config\UrlRewriteRules.config" />

You then place the rewrite rules in a file called UrlRewriteRules.config  in the App_Config folder:

<?xml version="1.0" encoding="utf-8"?>
  <clear />
  <rule name="ImageScaler" stopProcessing="true">
    <match url="^/scaleimage/([^/]*)/w/(\d+)/h/(\d+)/name/([a-zA-Z]+)\.([a-zA-Z]+)$" />
    <conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
    <action type="Rewrite" url="/Sitecore Modules/web/ImageResizer.ashx?id={R:1}&amp;width={R:2}&amp;height={R:3}&amp;name={R:4}&amp;extension={R:5}" appendQueryString="false" logRewrittenUrl="true" />

The “match” regular expression determines which URL to be redirected.
In this example I try to catch /scaleimage/1200/w/800/h/600/name/scaledimage.jpg.
The “rewrite” determines the URL to rewrite to (in this case /Sitecore Modules/web/ImageResizer.ashx) and the regular expression matches are appended as parameters to the .ashx page by the {R:n} syntax.

This was the URL Rewrite part, now onto some code.

I need to register my /Sitecore Modules/web/ImageResizer.ashx in Sitecore, and I can do this by adding the following to the web.config:

    <handler trigger="/Sitecore Modules/web/ImageResizer/" handler="ImageResizer" />
    <add verb="*" path="/Sitecore Modules/web/ImageResizer.ashx" type="MyCode.ImageResizer, MyCode" name="ImageResizer" />

The .ashx handler is ready to be called (example code only, will not compile):

namespace MyCode
  public class ImageResizer : IHttpHandler
    public void ProcessRequest(HttpContext context)
      // Retrieving the parameters from the request as defined in the 
      // "rewrite" rule:
      // ?id={R:1}&amp;width={R:2}&amp;height={R:3}&amp;name={R:4}&amp;extension={R:5}
      string ID = context.Request.QueryString["id"];
      string width = context.Request.QueryString["width"];
      string height = context.Request.QueryString["height"];
      string name = context.Request.QueryString["name"];
      string extension = context.Request.QueryString["extension"];

      // Do some code to get the image and resizing it
      Image image = GetSomeImaginaryImageClass();

      // Stream it back to the user
      context.Response.ContentType = "image/jpeg";
      context.Response.StatusCode = (int)HttpStatusCode.OK;
      context.Response.BufferOutput = true; 
      byte[] bytes = image.GetBytes();
      context.Response.OutputStream.Write(bytes, 0, bytes.Length);



Posted in .net, c#, General .NET, Sitecore 6, Sitecore 7 | Tagged , , , , | Leave a comment

Sitecore 7 create item and update index at the same time

With Sitecore 7, the Sitecore index (based on Lucene.NET) have greatly improved, thus making the index even more usable.

One thing you should know though, is that when you add items to Sitecore using the Sitecore.Data.Items.Item.Add() method, the index is not immediately updated. The index is updated shortly after, but the time varies depending on your index update strategy.
The result is that if you query the index just after you added a new item, it is not a given that the item exists in the index.

There is a solution to this (of course, this is Sitecore). When you have added the new item, you must manually update the search index for this specific item.

I have made an Extension Method to show how this can be done:

using System;
using System.Linq;
using Lucene.Net.Index;
using Sitecore.ContentSearch;
using Sitecore.Data;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;

namespace MyProject
  public static class ItemExtensions
    public static Item AddAndUpdate(this Item item, string name, TemplateID templateID)
      Assert.ArgumentNotNull(item, "item");
      Assert.IsTrue(item.Database.Name.ToLower() == "master", "Could not add item to " + item.Paths.FullPath + ": item is not in the 'master' database");
      Item newItem = item.Add(ItemUtil.ProposeValidItemName(name), templateID);
      Log.Audit("Item create: " + AuditFormatter.FormatItem(newItem), typeof(ItemExtensions));
      var tempItem = (SitecoreIndexableItem)newItem;
      return newItem;


The trick lies within these 2 lines of code:

var tempItem = (SitecoreIndexableItem)newItem;

First the item is converted into a SitecoreIndexableItem, and then the master index is refreshed for this single item.

The extension method can be used like this:

using MyProject;

namespace MyNamespace
  public void AddItem(Item parent, string name, TemplateID templateID)
    Item newItem = parent.AddAndUpdate(name, templateID);

Please note that this will increase the time to create an item substantially, from barely mesureable milliseconds to a full second or so.




Posted in .net, c#, Sitecore 7 | Tagged , , , , | 5 Comments

Unable to serialize the session state. In ‘StateServer’ and ‘SQLServer’ mode

When switching the sessionState mode of your web project from InProc to SQLServer you might encounter this error:

Exception: System.Web.HttpException
Message: Unable to serialize the session state. In ‘StateServer’ and ‘SQLServer’ mode, ASP.NET will serialize the session state objects, and as a result non-serializable objects or MarshalByRef objects are not permitted. The same restriction applies if similar serialization is done by the custom session state store in ‘Custom’ mode.

.NET handles the storage of session objects differently from InProc to SQLServer. When storing session objects in a SQL Server, .NET will serialize the objects. This is necessary because the session object needs to be transferred from server to server.

To support this, all you need is to mark the objects that are part of your sessions with the [Serializable]  attribute:

namespace MyNameSpace
  // Class is marked as serializable
  public class MyClass
    // Some code here

So how do you find the classes that need the [Serializable] attribute?

.NET is pretty good at telling which classes needs to be serializable in the nested exception:

Nested Exception

Exception: System.Runtime.Serialization.SerializationException
Message: Type ‘MyNamespace.MyClass‘ in Assembly ‘MyAssembly, Version=, Culture=neutral, PublicKeyToken=null’ is not marked as serializable.
Source: mscorlib



Sitecore does not as such support other modes than InProc session state. As Sitecore states (quote from the Sitecore Scaling Guide):

The Sitecore CMS user interfaces require in-process ASP.NET session management. For CM
instances, the value of the mode attribute of the /configuration/system.web/sessionState
element in the web.config file must be InProc.
In-process session management requires you to configure the CM load balancer for server affinity —
also known as sticky sessions. You can use other values for the mode attribute in the CD

This means that the Sitecore client and page editor is not tested using other session state methods than InProc. But since the content delivery servers do not touch the Sitecore UI, it is up to you to make the front end code compatible with StateServer or SQLServer methods.

The best session state management for Sitecore CD servers would still be InProc, and let your load balancer use sticky sessions.

Posted in c#, General .NET, Sitecore | Tagged , , , , , , | 1 Comment

Stream Sitecore media items to HttpResponse

Here is a small tip for you who is doing API’s based on Sitecore. How to get an item from the Sitecore Media Library and stream it to the HttpResponse of a page.

This is an example of an .ashx HttpHandler class that gets the ID of an Sitecore Media Library item and streams the item. Add your own exception handling as you wish.

using System;
using System.Net;
using System.Web;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Configuration;

namespace MyProject
  public class GetImage : IHttpHandler
    public void ProcessRequest(HttpContext context)
      // ID of media item
      string id = context.Request.QueryString["id"];

      // Get media item
      MediaItem item = (MediaItem)Factory.GetDatabase("web").GetItem(id);

      // Get name to be shown when image is saved
      string imageName = item.Name + "." + item.Extension;

      context.Response.ContentType = mediaItem.MimeType;
      context.Response.AppendHeader("Content-Disposition", string.Format("inline;filename=\"{0}\"", imageName));
      context.Response.StatusCode = (int)HttpStatusCode.OK;
      context.Response.BufferOutput = true;
      // Copy the media stream to the response output stream
      // As momma always said: Always remember to flush

    public bool IsReusable
        return false;

The trick lies within the type casting of an Item to a MediaItem. The MediaItem contains a media stream that can be copied to the OutputStream of the HttpResponse.

Another trick is to construct a file name from the item name + the file extension found on the MediaItem. Adding this to the output stream will make the name appear as the default file name when users saves your file.

Posted in .net, c#, Sitecore 7 | Tagged , , , | Leave a comment

Sitecore Code Generator and O/R mapper

You probably have never used a code generator for the same reason I never used a code generator:

  • It was not developed by yourself
  • It’s not generating the code you need
  • You cannot extend it with the stuff you need
  • They create the same code for all of your Sitecore templates
  • It’s difficult to install
  • It was not developed by yourself.

A code generator that creates an O/R relationship between your Sitecore templates and your code can save you a ton of work though:

  • Automatically update your code when new templates are created
  • There is never any mismatch between your code and Sitecore templates
  • Code class names matches Sitecore template names
  • All field names are mapped correctly

That is why I introduce the open-source, freeware, use-as-you-please:

SITECOREORM, The object/relational code generator for Sitecore.

You can download it here for free, including the source code:

SitecoreORM O/R Mapper and Code Generator


The SitecoreORM is a code generator framework that allows you to create code templates based on well-known user controls (.aspx) pages, and generate code from all of your Sitecore templates.

There is no Sitecore package; all you do is to place the code in your solution and call \sitecore modules\Shell\SitecoreORM\Execute.aspx to execute the code generation.

Out of the box, SitecoreORM generates simple classes that maps all fields on a Sitecore template, references to inherited templates, and a static struct of field names to be used by Sitecore’s sc:FieldRenderer.

But the idea behind SitecoreORM is that you define yourself how your classes should look like.

Changing the output is done by simply changing some .aspx pages. For example, the code template is an .aspx page. This is an example of how it looks (simplified, download the code for the complete example):

namespace <%# Namespace %>
  public class <%# ClassName %> : BaseItem
    public <%# ClassName %>(Item innerItem)
      InnerItem = innerItem;

    public Item InnerItem { get; private set; }

    public override FieldCollection Fields
      get { return InnerItem.Fields; }

    #region Inherited Templates
<%# ExecuteCodeProvider("SomeClass, SomeDll", "SomeParameter") %>

Generating fields, inherited templates or other thing you need, is done using code providers, that you plug in. SitecoreORM uses reflection to find the code, and your class only needs to implement an ICodeProvider interface that has 1 method: Get().

Here is an example of an .aspx file that generates a Sitecore field (a CheckBox field):

<%@ Page Language="C#" AutoEventWireup="true" %>
<%@ Import Namespace="Sitecore.Data.Items" %>
<%@ Import Namespace="SitecoreORM.CodeProviders" %>
<%@ Import Namespace="SitecoreORM.Services" %>
<script runat="server">

  void Page_Load(object sender, System.EventArgs e)

  private TemplateFieldItem Field
    get { return GenerateFieldProperties.Field; }

  private string PropertyName
    get { return NameNormalizerService.GetValidName(Field.Name); }


    /// <summary>
    /// <%# TemplateFieldItemCommentService.GetComment(Field) %>
    /// </summary>
    public Sitecore.Data.Fields.CheckboxField <%# PropertyName %>
      get { return InnerItem.Fields["<%# Field.Name %>"]; }

The SitecoreORM framework is written with the fewest lines of code that will do the job. The code is as simple as possible, making it easier to extend, and for you to understand.

Features of SitecoreORM includes:

  • O/R mapping Sitecore templates to C# code.
  • Every aspect of the code generation can be customized:
    • .aspx page based code generation allows you to choose the class format yourself.
    • Each field type from Sitecore can be mapped by .aspx pages, making it easy to determine how each field type from Sitecore should be mapped.
    • Inherited templates can be mapped as well.
    • Field names can be mapped to be used by sc:FieldRenderer
    • Plug in your own code; SitecoreORM uses reflection to find the code generators.
  • Customizable output using an include file (/app_config/include/sitecoreorm.config)
    • Determine class name suffixes
    • Determine name space prefixes
    • Determine removing of parts of template path from namespace
    • Determine file destination for generated classes
    • Specify which template folders to ignore (for example /system/ and /common/)
    • Specify which template folders to include
    • Specify file destination for each template folder to include, hence supporting component based development
  • Simple code. Less than 20 classes makes up the framework. 5 services and one repository does most of the job.

Check out SitecoreORM here:

Posted in c#, Sitecore, Sitecore 7 | Tagged , , , | 19 Comments