
Using NHibernate in Orchard.CMS
Orchard.CMS is one of the popular free open source .NET web content management systems. NHibernate is used as an ORM to access data. More detailed information can be found on the official website of the project , in addition, there were already articles on Habré devoted to Orchard.CMS.
Orchard CMS uses its own way to create a data schema through Migration and SchemeBuilder. To access the NHiberanate session (ISession) and transactions, specialized interfaces are used that encapsulate these objects inside (ISessionHolder and ITransactionManager). Own repository interfaces (IRepository) are organized, implementations of which work on top of NHibernate Linq Query.
Orchard does not provide direct access to NHibernate by default. Below we will consider the features of building and using a domain model based on Orchard CMS, as well as how to use NHibernate directly from its module.
If the business layer is encapsulated separately, and Orchard.CMS accesses entities using web services, the problem of constructing a domain model does not arise. This applies to large projects. The studies in this article will be valid for projects in which it is initially planned to use a common base for both Orchard CMS and business logic entities.
Domain model based on Orchard.CMS (ContentTypeDefinition)
Consider the BlogPost model in the basic module of the Blog in Orchard.CMS. (The source code of the project can be found on the official website). BlogPost Blog Type Model:
A widget is implemented in the blog module to display the last N blog entries. Let's look at the SQL query, which are formed to obtain this list. To do this, use NHProfiler and plug in the SoNerdy.NHProf module in Orchard.CMS. (One of the recommendations when developing on Orchard.CMS is to use the NHibernate Profiler www.hibernatingrhinos.com/products/nhprof . This utility is indispensable in analyzing and optimizing the site.)
A query that selects N blog entries is as follows. Join fields for additional content parts have been specifically removed to focus on the base parts.
Brief analysis of the request:
The result of the implementation of the domain model of the application within the framework of the Orchard.CMS content types will be the following:
As an option to solve the problem - completely abandon the use of content types when building a domain model. Its implementation using simple Record classes. Orchard uses AutoMapping to configure NHibernate, and one of the conventions is this: you must add the Record postfix to all data type names. The downside is that testing will still depend on the Orchard context, and migration to another content management system will be more complicated. In addition, it is necessary to implement separate modules for business logic and for presentation.
Using NHibernate directly in Orchard.CMS
Framework Orchard.CMS provides the ability to configure HNibernate and use its capabilities directly, without the Orchard pipeline. Starting with version 1.7, the new ISessionConfigurationEvents interface is now available. Example implementation of ISessionConfigurationEvents in a demo project:
To configure your module, you need to add an implementation of this interface to your module and define the NHibernate configuration in the Created method. It is also necessary to define a Hash module for automatic regeneration of the general NHibernate configuration. Orchard.CMS generates NHibernate configuration in the mapping.bin file, which is located in the App_Data \ Sites \ Default folder, a separate configuration for each site. To regenerate the existing configuration, you must delete the mapping file, and the application will create it automatically.
To access entities, it is possible to use existing interfaces in Orchard.CMS:
IRepository is a standard interface, an implementation that Linq To NHibernate Cacheble Query uses. The main methods:
ISessionLocator - An interface in Orchard.CMS that provides access to the ISession interface object. The main methods:
Mention should be made of transactions in Orchard. By default, Orchard.CMS creates one transaction for the entire request and performs its Commit after the request is completed. If the request is successful, commit is executed; if not, the transaction is rolled back. The default data isolation level is ReadCommitted. In order to complete the current transaction and open a new one, you must use the ITransactionManager interface. This interface provides methods for working with transactions.
It was possible to test and implement a demo project using the Fluent NHibernate Mapping configuration of a domain model defined in a separate assembly. The project is located on github and is available for download .
This article is a recommendation for the implementation of projects using Orchard.CMS. I would be glad if other effective approaches for implementing the domain model in the framework of this content management system are described in the comments.
Orchard CMS uses its own way to create a data schema through Migration and SchemeBuilder. To access the NHiberanate session (ISession) and transactions, specialized interfaces are used that encapsulate these objects inside (ISessionHolder and ITransactionManager). Own repository interfaces (IRepository) are organized, implementations of which work on top of NHibernate Linq Query.
Orchard does not provide direct access to NHibernate by default. Below we will consider the features of building and using a domain model based on Orchard CMS, as well as how to use NHibernate directly from its module.
If the business layer is encapsulated separately, and Orchard.CMS accesses entities using web services, the problem of constructing a domain model does not arise. This applies to large projects. The studies in this article will be valid for projects in which it is initially planned to use a common base for both Orchard CMS and business logic entities.
Domain model based on Orchard.CMS (ContentTypeDefinition)
Consider the BlogPost model in the basic module of the Blog in Orchard.CMS. (The source code of the project can be found on the official website). BlogPost Blog Type Model:
- BlogPost - type of content (BlogPost - ContentTypeDefinition). It consists of the following parts:
- BlogPostPart - the content part responsible for the description of the blog.
- CommonPart is a standard content part that encapsulates information about the author and version.
- PublishLaterPart - the content part for implementing drafts.
- TitlePart - the title part.
- AutoroutPart - Beautiful URLs.
- BodyPart - the actual body of the blog entry.
A widget is implemented in the blog module to display the last N blog entries. Let's look at the SQL query, which are formed to obtain this list. To do this, use NHProfiler and plug in the SoNerdy.NHProf module in Orchard.CMS. (One of the recommendations when developing on Orchard.CMS is to use the NHibernate Profiler www.hibernatingrhinos.com/products/nhprof . This utility is indispensable in analyzing and optimizing the site.)
A query that selects N blog entries is as follows. Join fields for additional content parts have been specifically removed to focus on the base parts.
SELECT top 12 ...
FROM v1__Orchard_Framework_ContentItemVersionRecord this_
inner join v1__Orchard_Framework_ContentItemRecord contentite1_
on this_.ContentItemRecord_id = contentite1_.Id
inner join v1__Common_CommonPartRecord commonpart3_
on contentite1_.Id = commonpart3_.Id
inner join v1__Orchard_Framework_ContentTypeRecord contenttyp2_
on contentite1_.ContentType_id = contenttyp2_.Id
WHERE contenttyp2_.Name in ('BlogPost' /* @p0 */)
and commonpart3_.Container_id = 22 /* @p1 */
and this_.Published = 1 /* @p2 */
ORDER BY commonpart3_.CreatedUtc desc
Brief analysis of the request:
- To obtain only basic information of a content type, at least three Inner Join are required.
- The basic structure of all content types defined in Orchard.CMS is contained in the tables: ContentItemVersionRecord, ContentItemRecord and ContentTypeRecord
The result of the implementation of the domain model of the application within the framework of the Orchard.CMS content types will be the following:
- All entities will have data in the same tables. For example, for e-commerce, Id products, orders, customers will be stored in the same tables.
- Even small queries will select all information about the content part. For example, if you need to get the name of the manufacturer to display under the product in the list. As part of the Orchard implementation, all data from the content part will be selected. Otherwise, the meaning of using the technique of dynamic representation (Shape) is lost.
- The task of building reports directly from the database is very complicated. A lot of Join.
- Migrating data using the database in the framework of this implementation is very. A lot of Join.
- Decreased performance due to excessive requests. A lot of attention needs to be paid to working with Profiler, to identify bottlenecks.
- Orchard.CMS is based on content parts and content types. Text sections, blogs, html parts and so on - in most cases, content types or content parts. Mixing presentation data and the application domain model is a gross violation of encapsulation.
- Transferring a domain model and business logic performed in the context of Orchard to another CMS or pure MVC is a huge job.
- Testing, it will have to be performed within the framework of the Orchard context.
As an option to solve the problem - completely abandon the use of content types when building a domain model. Its implementation using simple Record classes. Orchard uses AutoMapping to configure NHibernate, and one of the conventions is this: you must add the Record postfix to all data type names. The downside is that testing will still depend on the Orchard context, and migration to another content management system will be more complicated. In addition, it is necessary to implement separate modules for business logic and for presentation.
Using NHibernate directly in Orchard.CMS
Framework Orchard.CMS provides the ability to configure HNibernate and use its capabilities directly, without the Orchard pipeline. Starting with version 1.7, the new ISessionConfigurationEvents interface is now available. Example implementation of ISessionConfigurationEvents in a demo project:
public class PersistenceConfiguration : ISessionConfigurationEvents
{
public PersistenceConfiguration()
{
Logger = NullLogger.Instance;
}
public ILogger Logger { get; set; }
public void Created(FluentConfiguration cfg, AutoPersistenceModel defaultModel)
{
cfg.Mappings(x => x.FluentMappings.AddFromAssemblyOf());
}
...
public void ComputingHash(Hash hash)
{
hash.AddString("NHStore.Domain.Mapping");
}
}
To configure your module, you need to add an implementation of this interface to your module and define the NHibernate configuration in the Created method. It is also necessary to define a Hash module for automatic regeneration of the general NHibernate configuration. Orchard.CMS generates NHibernate configuration in the mapping.bin file, which is located in the App_Data \ Sites \ Default folder, a separate configuration for each site. To regenerate the existing configuration, you must delete the mapping file, and the application will create it automatically.
To access entities, it is possible to use existing interfaces in Orchard.CMS:
IRepository is a standard interface, an implementation that Linq To NHibernate Cacheble Query uses. The main methods:
- void Create (T entity); - Save
- void Update (T entity); - Evict / Merge
- void Delete (T entity); - Delete
- IEnumerable Fetch (); - ToReadOnlyCollection
- IQueryable Table {get; } - LinqToNHibernate Query Object
ISessionLocator - An interface in Orchard.CMS that provides access to the ISession interface object. The main methods:
- ISession For (Type entityType); - the type of entity is transmitted, which is used only for logging (Logger.Debug ("Acquiring session for {0}", entityType); A session object is created one for each request.
Mention should be made of transactions in Orchard. By default, Orchard.CMS creates one transaction for the entire request and performs its Commit after the request is completed. If the request is successful, commit is executed; if not, the transaction is rolled back. The default data isolation level is ReadCommitted. In order to complete the current transaction and open a new one, you must use the ITransactionManager interface. This interface provides methods for working with transactions.
It was possible to test and implement a demo project using the Fluent NHibernate Mapping configuration of a domain model defined in a separate assembly. The project is located on github and is available for download .
This article is a recommendation for the implementation of projects using Orchard.CMS. I would be glad if other effective approaches for implementing the domain model in the framework of this content management system are described in the comments.