Data Access - Fetch plans and strategies

The following blog post will shed some light on Data Access fetch plans and strategies used in Sitefinity.

Fetch Strategy

The database round trip is the first basic cost of retrieving data. If your application makes more round trips than are necessary, then the program will be slower than it could be. Fetch strategies are used to overcome unnecessary round trips to the database.

When you query for an object, you actually retrieve only the object you requested. The related objects are not automatically fetched at the same time. The Open Access FetchStrategy class provides you with immediate loading of related data. The FetchStrategy class provides two methods to achieve immediate loading of specified related data. The LoadWith and LoadWith methods allow immediate loading of data related to the main target.

A sample usage of the fetch strategy in Sitefinity is the retrieval of the page URLs with the page node. As a page can have additional URLs, fetching the collection might slow down the application. You could add a fetch optimization strategy on context level or on a query level.

Here's a sample code to set the fetch strategy on a context level:

using (var pageManager = PageManager.GetManager())
                // Set default fetch strategy for the context - context level
                var oaContext = pageManager.Provider.GetTransaction() as Telerik.Sitefinity.Data.OA.SitefinityOAContext;
                if (oaContext != null)
                    var fetchStrategy = new Telerik.OpenAccess.FetchOptimization.FetchStrategy();
                    fetchStrategy.LoadWith<PageNode>(p => p.Urls);
                    oaContext.FetchStrategy = fetchStrategy;

                var query = pageManager.GetPageNodes().Where(p => p.ParentId == parentPage.Id);

Here's a sample code to set the fetch strategy on a query level:

using (var pageManager = PageManager.GetManager())
                var query = pageManager.GetPageNodes()
                    .Where(p => p.ParentId == parentPage.Id)
                    .Include<PageNode>(p => p.Urls);

The code above uses the Include method to define fetch plan on a query level. You could either use the Include or LoadWith methods in one query.

Load Behavior

Another strategy to have in mind is how you are going to fetch the persistent properties. This is specified in the Fluent mappings associations via the WithLoadBehavior method.

There are three types of loading:

Default – the runtime will decide whether to load the persistent property based on the type of the property.
Lazy – loading on demand
Eager – immediate loading

Depending on the type and size of the property you may want to load it immediately or on demand.

Here's an example of setting eager loading in the message body of the email campaign:

var campaignMapping = new MappingConfiguration<Campaign>();          campaignMapping.HasAssociation<Telerik.Sitefinity.Newsletters.Model.MessageBody>(p => p.MessageBody).ToColumn("id3").WithLoadBehavior(LoadBehavior.Eager).IsDependent(); 

As the message body of the email campaign could be bigger, it is recommended to load it immediately with the campaign object. That way you prevent slowing down the system while waiting to fetch the property.

In other cases in Sitefinity, they explicitly set the LoadBehavior to Lazy. This means that the property will be fetched only on demand. Here's an example in Sitefinity's NewslettersFluentMapping:

        campaignMapping.HasAssociation<Campaign>(p => p.RootCampaign).ToColumn("rootCampaign_id").WithLoadBehavior(LoadBehavior.Lazy);

In the code above the RootCampaign property is intentionally fetched on demand as this property can be set only to differ issues and campaigns. Campaigns do not have RootCampain property and thus the property must not be fetched all the time.

Veronica Milcheva

I am a passionate Sitefinity blogger, developer and consultant. In my spare time I enjoy running and listening to music. My personal quote: There's no tough problem, just not enough coffee :)

Sofia, Bulgaria