Data Access - Fluent Mappings integration in Sitefinity

As most of you already know Sitefinity connects to relational databases via the Data Access ORM. The following blog post will set the beginning of a deeper digging into Data Access components. You will understand what are the Data Access Fluent mappings, their ingredients and how they are integrated in Sitefinity.

Telerik Data Access supports many types of mappings – Model-First (Forward), Database-First (Reverse), Round Trip (Mixed Mode) and Code Only depending on the ways you want to work with the database in your application. In Sitefinity, the Code Only mapping is used, also known as Fluent Mapping API.

What is common between the Entity framework, LINQ to SQL and Telerik Data Access is the representation of each table in the database with it's fields to a class with properties and methods. The bunch of classes that are generated and their relationships are forming the data model. However what Data Access provides in its Code Only scenario is the ability of creating your classes without touching a designer or a single piece of an XML. Thus you could only write classes and interfaces like in any other .NET application to build the mappings between the database tables and the model classes.

To implement the fluent mappings for your data provider, you must create the following classes: Fluent mappings class and Fluent metadatasource class.

Fluent mapping class

The Fluent mappings class defines the mapping for the data persistent model class. By convention you must add the name of the data persistent object in plural as a prefix followed by the FluentMappings to name your fluent mappings class. For example: ProductsFluentMapping or JobsFluentMapping. Your fluent mappings class must inherit the OpenAccessFluentMappingBase class in Sitefinity. That way you can inherit the GetMapping method to start building your fluent mappings. Here's an example of the fluent mappings that are created for the Sitefinity's Products sample:

public class ProductsFluentMapping : OpenAccessFluentMappingBase

    public ProductsFluentMapping(IDatabaseMappingContext context)
        : base(context)
    { }

    public override IList<MappingConfiguration> GetMapping() 
		var mappings = new List<MappingConfiguration>();
		return mappings;

    private void MapItem(IList<MappingConfiguration> mappings)
		var itemMapping = new MappingConfiguration<ProductItem>();
        itemMapping.HasProperty(p => p.Id).IsIdentity();
		itemMapping.MapType(p => new { }).ToTable("custom_products");
        itemMapping.HasProperty(p => p.Price);
        itemMapping.HasProperty(p => p.QuantityInStock);
		itemMapping.HasAssociation<Telerik.Sitefinity.Security.Model.Permission>(p => p.Permissions);
		itemMapping.HasProperty(p => p.InheritsPermissions);
		itemMapping.HasProperty(p => p.CanInheritPermissions);
        itemMapping.HasAssociation(p => p.Urls).WithOppositeMember("parent", "Parent").ToColumn("content_id").IsDependent().IsManaged();

		//map language data & published translations
		CommonFluentMapping.MapILifecycleDataItemFields<ProductItem>(itemMapping, this.Context);

    private void MapUrlData(IList<MappingConfiguration> mappings)
		var urlDataMapping = new MappingConfiguration<ProductItemUrlData>(); 
		urlDataMapping.MapType(p => new { }).Inheritance(InheritanceStrategy.Flat).ToTable("sf_url_data");

In the code above you first create a mapping configuration. Next, you start mapping databases with their fields to properties of the data persistent class. The MapType() method specifies the property to column mapping for members of the persistent type. Finally, the ToTable() method specifies the table name for this mapping configuration.

There are two type of Fluent Mappings – default and explicit. The default mapping specifies that all property mappings will be handled by the Telerik Data Access ORM. The explicit mapping means that you are going to explicitly specify the property to column mappings and explicitly specify the column names. If you are going to use the default mapping, you could leave the MapType method without parameters. The second overload accepts a lambda expression that allows you to specify the property to column mapping explicitly. Although the default mapping is quicker and easiest to create, in Sitefinity the explicit one is used to have more control over the column naming. Here's an example for the Products sample

itemMapping.MapType(p => new { }).ToTable("custom_products"); 

The IsIdentity method is used to specify which property of the object should be used as a primary key. In the Products sample here's the code that specifies that the Id property of the ProductItem class is going to be a primary key:

 itemMapping.HasProperty(p => p.Id).IsIdentity(); 

However in some cases you may want the identity to be represented by more than one properties (also known as Composite field identity) or if you do not supply an identity, Telerik Data Access uses internal mechanism for creating Ids (also known as Internal Identity).

The ToColumn method specifies the column name that this property is mapped to. In the Products sample, columns are mapped only to the custom_products table.

Note that the Fluent Mapping API allows you to map a class to more than one table in the database. This is handled by the second overload of the ToColumn() method that accepts two string parameters – column name and table name. For more information, see Multi-table Entities in Telerik Data Access.

The HasProperty() method is a way to get the configuration object property. Later you could specify different configurations for the property. For example whether the field will be nullable or not is handled by the IsNullable() and IsNotNullable() methods.

Fluent metadata source class

The Fluent metadata source class wraps the mappings and exposes them to the Open Access data provider. In Sitefinity there are several base metadata source classes that inherit the FluentMetadataSource class by Telerik Data Access. They add abstractions over the base class depending on different needs.

Depending on whether you want to provide the basic functionality of all content based modules as security and workflow, you must inherit the ContentBaseMetadataSource class. That way you can reuse the functionality of the BuildCustomMappings method by adding the fluent mapping to the mappings created in the base implementation. In Sitefinity all content items like Blogs, Lists, Forms, Pages, etc inherit the ContentBaseMetadataSource class.

Here's an example of the implementation of the BuildCustomMappings class in the Products sample:

protected override IList<IOpenAccessFluentMapping> BuildCustomMappings() { 
  var sitefinityMappings = base.BuildCustomMappings(); 
  sitefinityMappings.Add(new ProductsFluentMapping(this.Context)); 
  return sitefinityMappings; 

In other cases you could inherit the SecuredProviderMetadataSource base class if you need to provide the security class mappings (permissions, security root, etc) for all providers that implement the ISecuredObject interface. Metadata source classes in Sitefinity that inherit the SecuredProviderMetadataSource class are the TaxonomyMetadataSource for Taxonomies, NewsletterMetadataSource for Email campaigns module, ModuleBuilderMetadataSource for the Module Builder module etc.

Another option is to inherit the SitefinityMetadataSourceBase class. This class provides additional functionality over the Data Access FluentMetadataSource class which allows to alter mappings according to the targeted database using the IOAFluentMappingContext interface. It also automatically adds any registered artificial fields for the mapped classes. Samples of metadata source classes that inherit the SitefinityMetadataSourceBase class in Sitefinity are Ecommerce classes like ShippingMetadataSource, OrdersMetadataSource, CatalogMetadataSource and other modules like RecycleBinMetadataSource for Recycle Bin module, CommentsFluentMetadataSource for Comments, etc.

Now that you've learned what are the Fluent Mappings and their integration in Sitefinity in the next blog post you will learn what are the main types of Fluent mappings.

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