A simple read-only Sitecore data provider
An external data source can be integrated into Sitecore and represented as items by implementing a custom data provider. The custom data provider interacts with the external data source to define items, item hierarchies, handle actions and queries against those items. A custom data provider is built by inheriting from Sitecore's abstract DataProvider class and how much work the custom data provider should do depends on the problem you are addressing. You may only need to implement a data provider which overrides a single virtual method or possibly all of them.
One of the most basic forms of data provider is a read-only data provider and to demonstrate how easy data can be integrated into Sitecore we'll take a look at a very simple example, a data provider which takes some data from an in memory collection and transforms the data into items under a specific item in Sitecore's content tree.
This will involve the following steps:
- Create a Sitecore template which will be used for items generated by the data provider
- Create a root item in Sitecore to serve as a container for the items created by the data provider
- Create an in memory repository containing some data to integrate into Sitecore
- Create a custom data provider which will:
- Retrieve the data to be integrated into Sitecore
- Transform data into items in the Sitecore content tree
Sitecore templates
In this example we create two templates in Sitecore. One which will be used to create the root item for the data being integrated with the data provider and one for the actual items containing the data.
Data provider root item
Using the root item template an item is created in the Sitecore content tree which will be the parent item for our external data.

The external data
In this example the external data is coming from a simple in memory repository which simple provides a collections of objects.
01.namespace SitecoreCms.CodeSamples.DataProviders.SimpleReadOnlyData
02.{
03. public class Rocket
04. {
05. public string ParentId { get; set; }
06. public string RocketId { get; set; }
07. public string Name { get; set; }
08. public string Title { get; set; }
09. public string Description { get; set; }
10. }
11.}
My in memory repository is simply returns a collection of rockets:
using System.Collections.Generic;
namespace SitecoreCms.CodeSamples.DataProviders.SimpleReadOnlyData
{
public class RocketRepository
{
public IEnumerable<Rocket> GetSimpleRocketCollection()
{
var rockets = new List<Rocket>()
{
new Rocket()
{
ParentId = null,
RocketId = "1001",
Name = "Falcon 9",
Title = "The Falcon 9",
Description = "Falcon 9 is ...."
},
new Rocket()
{
ParentId = null,
RocketId = "1002",
Name = "Titan",
Title = "The Titan Rocket",
Description = "Titan was ...."
}
};
return rockets;
}
}
}
The read-only data provider
Create a new class
Create a class which inherits from Sitecore.Data.DataProviders.DataProvider
using Sitecore.Data.DataProviders;
namespace SitecoreCms.CodeSamples.DataProviders.SimpleReadOnlyData
{
public class RocketReadOnlyDataProvider : DataProvider
{
}
}
Add a constructor
I would like to be able to configure the data provider using a config file which passes the database name, IDTablePrefix, rockets root item template an rocket template IDs so I create a constructor with some parameters and create an instance of the rocket repository:
using System;
using System.Linq;
using Sitecore.Data;
using Sitecore.Data.DataProviders;
using Sitecore.Diagnostics;
using Sitecore.Data.IDTables;
using Sitecore.Data.Items;
using System.Collections.Generic;
namespace SitecoreCms.CodeSamples.DataProviders.SimpleReadOnlyData
{
public class RocketReadOnlyDataProvider : DataProvider
{
private readonly string _targetDatabaseName;
private readonly string _idTablePrefix;
private readonly ID _rocketTemplateID;
private readonly ID _rocketRootTemplateID;
private readonly IEnumerable<Rocket> _rockets;
public RocketReadOnlyDataProvider(string targetDatabaseName, string rocketRootTemplateID, string rocketTemplateID, string idTablePrefix)
{
Assert.ArgumentNotNullOrEmpty(targetDatabaseName, "targetDatabaseName");
Assert.ArgumentNotNullOrEmpty(rocketRootTemplateID, "rootTemplateId");
Assert.ArgumentNotNullOrEmpty(rocketTemplateID, "simpleReadOnlyDataTemplateId");
Assert.ArgumentNotNullOrEmpty(idTablePrefix, "idTablePrefix");
_targetDatabaseName = targetDatabaseName;
_idTablePrefix = idTablePrefix;
if (!ID.TryParse(rocketRootTemplateID, out _rocketRootTemplateID))
throw new InvalidOperationException(string.Format("Invalid rocket root template ID {0}", rocketRootTemplateID));
if (!ID.TryParse(rocketTemplateID, out _rocketTemplateID))
throw new InvalidOperationException(string.Format("Invalid rocket template ID {0}", rocketTemplateID));
_rockets = new RocketRepository().GetSimpleRocketCollection();
}
}
}
Override GetItemDefinition
This method is responsible for building an item definition based on the data to be integrated into Sitecore.
public override ItemDefinition GetItemDefinition(ID itemID, CallContext context)
{
Assert.ArgumentNotNull(itemID, "itemID");
// Retrieve the rocket id from Sitecore's IDTable
var rocketId = GetRocketIdFromIDTable(itemID);
if (!string.IsNullOrEmpty(rocketId))
{
// Retrieve the rocket data from the rockets collection
var rocket = _rockets.FirstOrDefault(o => o.RocketId == rocketId);
if (rocket != null)
{
// Ensure the rocket item name is valid for the Sitecore content tree
var itemName = ItemUtil.ProposeValidItemName(rocket.Name);
// Return a Sitecore item definition for the rocket using the rocket template
return new ItemDefinition(itemID, itemName, ID.Parse(_rocketTemplateID), ID.Null);
}
}
return null;
}
private string GetRocketIdFromIDTable(ID itemID)
{
var idTableEntries = IDTable.GetKeys(_idTablePrefix, itemID);
if (idTableEntries.Any())
return idTableEntries[0].Key.ToString();
return null;
}
Override GetItemVersions
We need to provide some information which tells Sitecore which versions content is available in and in what languages. This is done by overriding the GetItemVersions method, here using just a little hack for sake of example.
public override VersionUriList GetItemVersions(ItemDefinition item, CallContext context)
{
if (CanProcessItem(item.ID))
{
VersionUriList versions = new VersionUriList();
// Just a little hack
versions.Add(Language.Current, Sitecore.Data.Version.First);
context.Abort();
return versions;
}
return base.GetItemVersions(item, context);
}
Override GetChildIDs
This method creates a list of item IDs representing the children of an item.
public override IDList GetChildIDs(ItemDefinition parentItem, CallContext context)
{
if (CanProcessParent(parentItem.ID))
{
var itemIdList = new IDList();
foreach (var rocket in _rockets)
{
var rocketId = rocket.RocketId;
// Retrieve the Sitecore item ID mapped to his rocket
IDTableEntry mappedID = IDTable.GetID(_idTablePrefix, rocketId);
if (mappedID == null)
{
// Map this rocket to a Sitecore item ID
mappedID = IDTable.GetNewID(_idTablePrefix, rocketId, parentItem.ID);
}
itemIdList.Add(mappedID.ID);
}
context.DataManager.Database.Caches.DataCache.Clear();
return itemIdList;
}
return base.GetChildIDs(parentItem, context);
}
private bool CanProcessParent(ID id)
{
var item = Factory.GetDatabase(_targetDatabaseName).Items[id];
bool canProcess = false;
if (item.Paths.IsContentItem && item.TemplateID == _rocketRootTemplateID)
{
canProcess = true;
}
return canProcess;
}
Override GetParentID
This method returns the parent id stored in the IDTable.
public override ID GetParentID(ItemDefinition itemDefinition, CallContext context)
{
var idTableEntries = IDTable.GetKeys(_idTablePrefix, itemDefinition.ID);
if (idTableEntries.Any())
{
return idTableEntries.First().ParentID;
}
return base.GetParentID(itemDefinition, context);
}
Override GetItemFields
In order to populate the item fields with rocket data, we need to build a collection of item fields and map the rocket property values to these fields:
public override FieldList GetItemFields(ItemDefinition itemDefinition, VersionUri version, CallContext context)
{
var fields = new FieldList();
var idTableEntries = IDTable.GetKeys(_idTablePrefix, itemDefinition.ID);
if (idTableEntries.Any())
{
if (context.DataManager.DataSource.ItemExists(itemDefinition.ID))
{
ReflectionUtil.CallMethod(typeof(ItemCache), CacheManager.GetItemCache(context.DataManager.Database), "RemoveItem", true, true, new object[] { itemDefinition.ID });
}
var template = TemplateManager.GetTemplate(_rocketTemplateID, Factory.GetDatabase(_targetDatabaseName));
if (template != null)
{
var rocketId = GetRocketIdFromIDTable(itemDefinition.ID);
if (!string.IsNullOrEmpty(rocketId))
{
var rocket = _rockets.FirstOrDefault(o => o.RocketId == rocketId);
if (rocket != null)
{
foreach (var field in GetDataFields(template))
{
fields.Add(field.ID, GetFieldValue(field, rocket));
}
}
}
}
}
return fields;
}
protected virtual IEnumerable<TemplateField> GetDataFields(Template template)
{
return template.GetFields().Where(ItemUtil.IsDataField);
}
private string GetFieldValue(TemplateField field, Rocket rocket)
{
string fieldValue = string.Empty;
switch (field.Name)
{
case "Title":
fieldValue = rocket.Title;
break;
case "Description":
fieldValue = rocket.Description;
break;
default:
break;
}
return fieldValue;
}
Override GetLanguages(CallContext context)
Our simple data provider is only being used to grab some external data and integrate it into the Sitecore content tree but a data provider also has the responsibility of retrieving the languages content could be made available in. If we do not do anything to disable this in our data provider then our data provider will get the languages defined in Sitecore along with any other data providers configured. This results in duplicate languages appearing in the client.

To get around this we simply need to override the GetLanguages method in our data provider.
public override LanguageCollection GetLanguages(CallContext context)
{
return null;
}
And now the duplicate languages are gone.

Sitecore configuration
All that is left now is to register the new provider with Sitecore by creating a config file in the /App_Config/Include folder:
<sitecore>
<dataProviders>
<rocketReadOnlyDataProvider type="SitecoreCms.CodeSamples.DataProviders.SimpleReadOnlyData.RocketReadOnlyDataProvider, SitecoreCms.CodeSamples.DataProviders">
<param desc="targetDatabaseName">master</param>
<param desc="rocketRootTemplateID">{FBE22B26-1774-4F61-8DD4-434F691D0C8B}</param>
<param desc="rocketTemplateID">{8DED8CEB-7140-4100-892D-52DD840FAF34}</param>
<param desc="idTablePrefix">Rockets</param>
</rocketReadOnlyDataProvider>
</dataProviders>
<databases>
<database id="master" singleInstance="true" type="Sitecore.Data.Database, Sitecore.Kernel">
<dataProviders hint="list:AddDataProvider">
<dataProvider patch:before="*[1]" ref="dataProviders/rocketReadOnlyDataProvider"/>
</dataProviders>
</database>
</databases>
</sitecore>
</configuration>
Open up the Sitecore content editor to verify the data has been integrated into the content tree.

And that's it job done! Well not quite, this is a very simple example of how to integrate external data in Sitecore with a data provider and there is a lot more to address such as generating an hierarchical structure of items, editing external data from the Sitecore client, performance, caching and publishing.
Source code
You can find the source code for this simple data provider in the code sample repository and navigate to the RocketReadOnlyDataProvider class:
Sitecore7CodeSamples / SitecoreCms.CodeSamples.DataProviders / SimpleReadOnlyData / RocketReadOnlyDataProvider.cs
I am always fiddling around with the code so it may vary a little from the above but I am sure you'll get the idea.