Using SPContext.Current is no guarantee for great performance


It is a common best practice, while working with the SharePoint server API, to always use context objects whenever possible. Because they have been already instantiated by SharePoint itself, reusing context data doesn’t cause additional calls to the database and allows you to create good performing solutions. However, just because you use SPContext.Current in your code, doesn’t mean your solution is built properly.

An easy example of how things can go wrong

Imagine the following scenario. You have a Page Library with some Publishing Pages in it that you provisioned together with your Web Template. Every page has a column called MetaDescription. You were given the task of generating a meta description tag in the head section containing the contents of the MetaDescription column.

To include the value of the MetaDescription column as a meta tag in your page you created a custom control and added it to the head section of the page in the Master Page. The code of the custom control might resemble the following snippet:

public class MetaDescription : Control {
    protected override void Render(HtmlTextWriter writer) {
        string description = null;

        using (new SPMonitoredScope("Load Meta Description")) {
            description = SPContext.Current.ListItem["MetaDescription"] as string; 
        }

        if (!String.IsNullOrEmpty(description)) {
            writer.AddAttribute(HtmlTextWriterAttribute.Name, "description");
            writer.AddAttribute(HtmlTextWriterAttribute.Content, description);
            writer.RenderBeginTag(HtmlTextWriterTag.Meta);
            writer.RenderEndTag();
        }
    }
}

When you refresh the page and view the HTML source, you should see the meta description tag rendered – just as expected. There is however one thing behind the scenes that isn’t working as expected.

If you take a closer look at the ULS log while loading your page you will see the following warning:

Performance warning about code causing an extra roundtrip to the database to retrieved data

How come that an additional roundtrip is being caused in spite of the fact that we are using the SPContext.Current.ListItem in our code?

Under the hood

It turns out that whenever you request a Publishing Page, SharePoint will preload the data from that page so that you can retrieve it quickly without causing extra calls to the database. In this process SharePoint uses information about the Content Type of the requested Publishing Page. The content of every column that belongs to that Content Type is being preloaded for quick retrieval.

However, if you try to load data from outside the Content Type SharePoint will need to make additional roundtrip to the database to retrieve that data. If you measure the time required to display the data that hasn’t been preloaded you can easily see how big the performance impact is.

Comparing the time to retrieve additional data (33ms) versus using data from the context (0.03ms)

A common reason for causing additional roundtrips to the database is when you don’t bind your custom Content Type to the List prior to creating a Publishing Page or you don’t set explicitly the Content Type while provisioning Publishing Pages declaratively. Although the page will be created, if you take a closer look at its Content Type you will see that it’s using the standard Page Content Type rather than your custom Content Type.

Publishing Page using the standard Page Content Type instead of a custom one

The Solution

To avoid additional roundtrips to the Content Database, when working with context data you should ensure that all the data is available within the Content Type of the context page and that the page is using the right Content Type. When provisioning Publishing Pages you should always ensure that the Content Type has been bound to the list. To verify that everything is working as expected you should check the ULS log on regular during the development process to see if no additional roundtrips to the database are being caused.

Comparing loading time of a standard field (0.01ms) versus custom field (0.04ms) after configuring the Content Type correctly.
Others found also helpful: