The content you're reading is getting on in years
This post is on the older side and its content may be out of date.
Be sure to visit our blogs homepage for our latest news, updates and information.
UPDATE: After the initial version of this blog post, we introduced some changes in Sitefinity, which required somewhat different code. Some people noticed this in the comments below. The blog post has been updated with code working for Sitefinity 4.1. You can always find a working code sample illustrating the topic in the latest release of the Products module in Sitefinity SDK.
As you know, Sitefinity relies heavily on the OpenAccess ORM for its data layer. We’ve provided an extensible model, in which you can plug a custom implementation for your storage mechanism, but all our modules use OpenAccess by default. That’s why we are excited to announce that we’re shipping a new version of OpenAccess with the 4.1 release.
There will be tons of benefits in this new version that we’ll announce later. For now, we want to bring your attention to a particular change we’re introducing with this upgrade - Fluent mappings for persistent classes. We’ve updated all of our built-in modules to use the new mappings, and all custom modules that use OpenAccess will have to do the same.
This means anyone who has created a custom Sitefinity 4.x module that relies on OpenAccess will need to make changes to their module before it is compatible with 4.1.
In this blog post we’ll look at the following topics:Let's get started...
To illustrate the value of fluent mapping support, let's look at how persistent classes were mapped until now. OpenAccess needs metadata to know how to generate tables from your persistent classes. In the current Sitefinity projects, there are two places where it gets the metadata from:
This is cumbersome for two reasons. First, you need to maintain both the code of the persistent class and the app.config file. Second, the only way to provide different mappings for a single class is to duplicate the whole mapping.
Let’s take the sample Products module from the Sitefinity SDK as an example. Its current mappings look like this:
<
mappings
current
=
"mssqlMapping"
>
<
mapping
id
=
"mssqlMapping"
>
<
namespace
name
=
"ProductCatalogSample.Model"
>
<
class
name
=
"ProductItem"
>
<
extension
key
=
"db-table-name"
value
=
"sfex_product_item"
/>
<
field
name
=
"permissions"
>
<
collection
>
<
extension
key
=
"db-link-table"
/>
</
collection
>
</
field
>
<
field
name
=
"urls"
>
<
collection
>
<
extension
key
=
"inverse"
value
=
"parent"
/>
<
extension
key
=
"managed"
value
=
"true"
/>
</
collection
>
</
field
>
<
field
name
=
"permissionChildren"
>
<
collection
>
<
extension
key
=
"db-link-table"
/>
</
collection
>
</
field
>
</
class
>
<
class
name
=
"ProductItemUrlData"
>
<!--<extension key="db-table-name" value="sfex_product_urls" />-->
<
field
name
=
"parent"
>
<
extension
key
=
"db-ref"
value
=
"Telerik.Sitefinity.GenericContent.Model.Content.contentId"
>
<
extension
key
=
"db-column"
>
<
extension
key
=
"db-column-name"
value
=
"content_id"
/>
</
extension
>
</
extension
>
</
field
>
</
class
>
</
namespace
>
</
mapping
>
</
mappings
>
Fluent mappings address all of the above challenges. They provide a single place where all persistent classes are configured. Joshua Holt has created a series of videos introducing Fluent mappings in the OpenAccess ORM. I highly encourage you to watch those before you start changing your modules. Below, we’ll put the same concepts into the Context of a Sitefinity module.
With the newly introduced fluent mappings, all of the above is replaced with code. Instead of relying on a particular XML file, the framework calls a method that you write. You can then supply the mapping and include your own logic depending on the database (e.g. map something differently for Oracle). The steps you need to follow are the following:
This property provides runtime state information used by Sitefinity. You just need to have it – nothing fancy in the implementation, except an automatic getter and setter.
public
OpenAccessProviderContext Context
{
get
;
set
;
}
This method is called automatically and returns a MetadataSource object, containing all information about the mappings in the specific provider. The object will be of the type you implement in the last step. Here just create an instance by passing the context parameter.
public
MetadataSource GetMetaDataSource(IDatabaseMappingContext context)
{
return
new
ProductsFluentMetadataSource(context);
}
public
class
ProductsFluentMapping : OpenAccessFluentMappingBase
{
public
ProductsFluentMapping(IDatabaseMappingContext context)
:
base
(context)
{ }
public
override
IList<MappingConfiguration> GetMapping()
{
var mappings =
new
List<MappingConfiguration>();
MapItem(mappings);
MapUrlData(mappings);
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.HasAssociation<Telerik.Sitefinity.Security.Model.PermissionsInheritanceMap>(p => p.PermissionChildren);
itemMapping.HasProperty(p => p.CanInheritPermissions);
itemMapping.HasAssociation(p => p.Urls).WithOppositeMember(
"parent"
,
"Parent"
).ToColumn(
"content_id"
).IsDependent().IsManaged();
itemMapping.HasAssociation<Telerik.Sitefinity.Workflow.Model.Tracking.ApprovalTrackingRecordMap>(p => p.ApprovalTrackingRecordMap);
mappings.Add(itemMapping);
}
private
void
MapUrlData(IList<MappingConfiguration> mappings)
{
var urlDataMapping =
new
MappingConfiguration<ProductItemUrlData>();
urlDataMapping.MapType(p =>
new
{ }).Inheritance(InheritanceStrategy.Flat).ToTable(
"sf_url_data"
);
mappings.Add(urlDataMapping);
}
}
This is the implementation of the mapping itself. All mappings inherit OpenAccessFluentMappingBase and override the GetMapping() method. In the sample Products module, we have separated its work in two calls, one for each persistent class. Everything you need to specify in a mapping has a corresponding method in the MappingConfiguration class. You can specify the name of the table, properties, any associations to other classes, inheritance configurations, etc. When developing a content-based module, they are going to be very similar to the mappings for Products in the above sample.
public
class
ProductsFluentMetadataSource : ContentBaseMetadataSource
{
public
ProductsFluentMetadataSource():
base
(null)
{ }
public
ProductsFluentMetadataSource(IDatabaseMappingContext context)
:
base
(context)
{ }
protected
override
IList<IOpenAccessFluentMapping> BuildCustomMappings()
{
var sitefinityMappings =
base
.BuildCustomMappings();
sitefinityMappings.Add(
new
ProductsFluentMapping(this.Context));
return
sitefinityMappings;
}
}
The metadata source is an object that you return in the GetMetadataSource method you implemented in the second step above. If you are building a custom module from scratch, you can directly create an instance of any class that derives from MetadataSource. However, when building content-based modules, most of the work has been done for you anyway. We’ve provided a way for you to reuse it – just create your own metadata source and inherit from ContentBaseMetadataSource. Then your only job is to override BuildCustomMappings() and add an instance of the mapping class you created in the previous step to the ones created in the base implementation (Generic Content).
Please note that all modules working with OpenAccess need to edit their project files and supply the correct path to the OpenAccess assemblies. For more information on this, refer to our documentation.
The source code for the new Products module mappings is available in the 4.1 SDK. Once you follow the steps above, your module should be ready for Sitefinity 4.1. Have these changes in mind before upgrading your projects to the release coming up next week. We’ll be available to help you if you have questions about the new OpenAccess version. We’re looking forward to seeing how you extend Sitefinity. Happy coding.
View all posts from The Progress Team on the Progress blog. Connect with us about all things application development and deployment, data integration and digital business.
Let our experts teach you how to use Sitefinity's best-in-class features to deliver compelling digital experiences.
Learn MoreSubscribe to get all the news, info and tutorials you need to build better business apps and sites