Orchard.CMS is one of the popular free open source web content management systems based on .NET. NHibernate is used as the ORM for data access. More detailed information can be found on the
official website of the project , besides, Habré already had articles on Orchard.CMS.
Orchard CMS uses its own method of creating a data scheme through Migration and SchemeBuilder. To access the NHiberanate session (ISession) and transactions, specialized interfaces are used that encapsulate these objects inside (ISessionHolder and ITransactionManager). We have organized our own repository interfaces (IRepository), whose implementations run on top of NHibernate Linq Query.
Orchard does not provide direct access to NHibernate by default. Below will be considered the features of the construction and use of a domain model based on Orchard CMS, as well as the method of using NHibernate directly from its module.
If the business layer is encapsulated separately, and Orchard.CMS addresses entities via web services, the problem of building 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.
Orchard.CMS (ContentTypeDefinition) domain modelConsider the BlogPost model in the basic Blog module in Orchard.CMS. (The source code of the project can be found on the official site). BlogPost Blog Type Model:
- BlogPost - content type (BlogPost - ContentTypeDefinition). It consists of the following parts:
- BlogPostPart - content part responsible for the blog description.
- CommonPart - the standard content part, encapsulates information about the author and version.
- PublishLaterPart - content part for the implementation of drafts.
- TitlePart - title part.
- AutoroutPart - beautiful URL.
- BodyPart - the actual blog entry body.
The blog module is implemented widget allows you to display the latest N blog entries. Let's look at the SQL query that is being formed to get this list. To do this, we use NHProfiler and connect the SoNerdy.NHProf module to 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 of additional content parts were specially 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' ) and commonpart3_.Container_id = 22 and this_.Published = 1 ORDER BY commonpart3_.CreatedUtc desc
')
A brief analysis of the request:
- To obtain only basic information of the content type, at least three Inner Join are necessary.
- 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 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 the 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 presentation (Shape) is lost.
- The task of building reports directly from the database is very complicated. A lot of Join.
- Data migration by means of the base within 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 the presentation data and the domain model of the application is a gross violation of encapsulation.
- To transfer a domain model and business logic, made in the context of Orchard, to another CMS or pure MVC is a great job.
- Testing, it will have to be performed within the Orchard context.
As a solution to 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 as follows: Record postfix must be added to all data type names. The downside is that testing will still depend on the Orchard context, and the migration to another content management system will become more complicated. In addition, it is necessary to implement separate modules for business logic and for presentation.
Using NHibernate directly in Orchard.CMSThe Orchard.CMS Framework provides the ability to configure HNibernate and use its capabilities directly, without the Orchard pipeline. Starting with version 1.7, the new interface ISessionConfigurationEvents has become available. An example implementation of ISessionConfigurationEvents in a demonstration 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<Customer>()); } ... public void ComputingHash(Hash hash) { hash.AddString("NHStore.Domain.Mapping"); } }
To configure your module, you need to add the implementation of this interface to your module and define the configuration of NHibernate in the Created method. You also need to define the Hash module for automatic regeneration of the overall configuration of NHibernate. Orchard.CMS generates the 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 an 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, the implementation used by Linq To NHibernate Cacheble Query. Basic 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 is an interface in Orchard.CMS that provides access to an ISession interface object. Basic methods:
- ISession For (Type entityType); - an entity type is passed that is used only for logging (Logger.Debug ("Acquiring session for {0}", entityType); A session object is created one for each request.
It is necessary to mention transactions in Orchard. By default, Orchard.CMS creates one transaction for the entire request and executes its Commit after the completion of the request. If the request is executed successfully - 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 need to use the ITransactionManager interface. This interface provides methods for working with transactions.
It was possible to test and implement a demonstration project using the Fluent NHibernate Mapping configuration of a domain model defined in a separate assembly. The project is on github and is available for
download .
This article is a recommendation to implement projects using Orchard.CMS. I would be glad if the comments describe other effective approaches for implementing the domain model within this content management system.