KNOWLEDGE BASE

Linq to Umbraco


Intro

We've been working on 3 larger Umbraco sites in which we've implemented our own Linq to Umbraco strategy. It's a fairly simple strategy, it doesn't involve custom expression trees or anything, it simply leverages Linq to Xml, IEnumerable<T> and extension methods. In simple terms, we basically convert Xml nodes into our own objects which we can then run typed Linq queries against. Since the performance of deserialization of this Xml into objects was one of our concerns, we implemented Microsoft's Policy Injection framework to handle the caching of our Umbraco objects which works really well (more on this later). Here's a quick example of what this allows you to do with Umbraco data (in this case Umbraco is storing information about events, such as festivals, etc...)

var todayEvents =(from e inGetEvents()
                   
where e.FromDate.Date==DateTime.Now.Date
                   
select e).ToList();

Background

In order for this to work, there is a bit of setup involved. The way that we've gone about this implementation is by defining a data model for each Document Type that you want to be able to use in this framework. In this example, Umbraco will need to be setup with a few Document Types: Event, EventComment, EventOrganizer, EventsContainer. I've included the definitions of these doc types in the source code so you can easily import them. For an event model, we would create an interface:

public interface IUmbEvent:IUmbracoItem 
{
   
System.Collections.Generic.ListEventComments{ get; set; }
   
string EventTitle{get;set;}
   
DateTime FromDate{get;set;}
   
string FullDescription{get;set;}
   
string ShortDescription{get;set;}
   
bool ShowInListing{get;set;}
   
DateTime? ToDate{get;set;}
}

You'll notice this interface extends IUmbracoItem which I've defined in our code library. It contains all of the basic properties of an Umbraco node:

public interface IUmbracoItem 
{
   
DateTime CreatedDate { get; set; }
   
int CreatorId { get; set; }
   
string CreatorName { get; set; }
   
int Id { get; set; }
   
int Level { get; set; }
   
System.Collections.Generic.DictionaryNodeData { get; }
   
string NodeName{ get;set; }
   
int NodeType{ get;set; }
   
string NodeTypeAlias{ get; set; }
   
int ParentId { get; set; }
   
string Path{ get; set; }
   
int SortOrder { get; set;}
   
int? Template { get; set;}
   
DateTime UpdateDate{ get; set; }
   
string UrlName { get; set;}
   
string Version { get; set;}
   
string WriterName { get; set;}
}

Next I've created a class that implements this interface called UmbEvent which extends UmbracoItem(which in turn implements IUmbracoItem). The constructor function of these objects are what does the deserialization:

public UmbEvent(XElement x) 
           
:base(x)
{
   
//Get the from date and use the DateConverter to convert it.
   
//If the returned date is MinValue, then the value in Umbraco has not actually
   
//been set and since it is mandatory, we'll throw an Exception.
   
FromDate = x.UmbSelectDataValue("FromDate",DateConverter);
   
if(FromDate == DateTime.MinValue)
       
throw new Exception(string.Format(
               
"The node with id {0} does not have a start date set",
               
this.Id.ToString()));

   
//Get the ToDate using a NullableDateTimeConverter
   
ToDate= x.UmbSelectDataValue("ToDate",NullableDateTimeConverter);

   
EventTitle= x.UmbSelectDataValue("EventTitle");
   
ShortDescription= x.UmbSelectDataValue("ShortDescription");
   
FullDescription= x.UmbSelectDataValue("FullDescription");
   
   
//Get the ShowInListing using the IntConverter since the value stored in Umbraco is
   
//an integer, not a boolean.
   
ShowInListing= x.UmbSelectDataValue("ShowInListing",IntConverter)==1;            
   
   
//Select all child "node" nodes that have a node type alias
   
//of "Event Comment" from the current element, deserialize them
   
//to UmbEventComment objects, and store them in our List property.
   
EventComments= x.UmbSelectNodes()
       
.UmbSelectNodesWhereNodeTypeAlias("EventComment")
       
.Select(n =>newUmbEventComment(n))
       
.ToList();            
}

In the code for the UmbracoItem class are some helper methods for data conversion so exceptions are not thrown and that data is converted properly. 

The next step is to setup the data service layer. I generally create a different service class for each model and in this example we'll have IUmbEventService interface with an UmbEventService class defined as:

public interface IUmbEventService:IUmbracoService 
{
   
intCreateEvent(IUmbEvent cqEvent);
   
voidUpdateEvent(int eventId,IUmbEvent e);
   
ListGetEvents();
   
IUmbEventGetEvent(int eventId);
   
DateTimeLastEventDate{get;}
}

Need an Umbraco Master?

Here at Simon Antony, we have an in house certified Umbraco Grand Master available for hire. Got a problem with your site, need architecture advice, give us a call to speak to Simon directly and see how we can help

Contact Simon Today!