ISessionConfigurationEvents

NHibernate

As you might know, Orchard uses NHibernate, a mature open source ORM providing many features.

Out of the box, Orchard implements a number of so-called conventions, which allow you, the developer, to simply start creating entity classes without having to configure the mapping yourself, be it through configuration files or code.

In almost all cases the established configuration works great, but what if you for example did want to customize the mapping of a specific entity type?
For example, given a Customer and a Product class:

 1: public class Customer {
2: public virtual int Id { get; set; }
3: public virtual string Name { get; set; }
4: }
5:  
6: public class Product {
7: public virtual int Id { get; set; }
8: public virtual Customer Customer { get; set; }
9: }

To map these entities, you would need two tables with the following columns:

Customer
Id (int, primary key)
Name (nvarchar)

Product
Id (int, primary key)
Customer_Id (int) 

Notice that the convention is to map navigation properties to a column by using the property name ("Customer") appended with "_Id".
Now let's say we want to override that convention for just our Product class. Where would we start?

ISessionConfigurationEvents

As it so happens to be, a few days after my birthday earlier this year, our good friend Piotr added an extensibility point, enabling us to customize the configuration of NHibernate by means of implementing an interface called ISessionConfigurationEvents:

 1: /// 
 2: /// Allows hooking into NHibernate session configuration pipeline.
 3: /// 
 4: public interface ISessionConfigurationEvents : ISingletonDependency {
 5:     /// 
 6:     /// Called when an empty fluent configuration object has been created, 
 7:     /// before applying any default Orchard config settings (alterations, conventions etc.).
 8:     /// 
 9:     ///Empty fluent NH configuration object.
 10:     ///Default persistence model that is about to be used.
 11:     void Created(FluentConfiguration cfg, AutoPersistenceModel defaultModel);
 12:  
 13:     /// 
 14:     /// Called when fluent configuration has been prepared but not yet built. 
 15:     /// 
 16:     ///Prepared fluent NH configuration object.
 17:     void Prepared(FluentConfiguration cfg);
 18:  
 19:     /// 
 20:     /// Called when raw NHibernate configuration is being built, after applying all customizations.
 21:     /// Allows applying final alterations to the raw NH configuration.
 22:     /// 
 23:     ///Raw NH configuration object being processed.
 24:     void Building(Configuration cfg);
 25:  
 26:     /// 
 27:     /// Called when NHibernate configuration has been built or read from cache storage (mappings.bin file by default).
 28:     /// 
 29:     ///Final, raw NH configuration object.
 30:     void Finished(Configuration cfg);
 31:  
 32:     /// 
 33:     /// Called when configuration hash is being computed. If hash changes, configuration will be rebuilt and stored in mappings.bin.
 34:     /// This method allows to alter the default hash to take into account custom configuration changes.
 35:     /// 
 36:     /// 
 37:     /// It's a developer responsibility to make sure hash is correctly updated when config needs to be rebuilt.
 38:     /// Otherwise the cached configuration (mappings.bin file) will be used as long as default Orchard configuration 
 39:     /// is unchanged or until the file is manually removed.
 40:     /// 
 41:     ///Current hash object
 42:     void ComputingHash(Hash hash);
 43: }

And since the comments are so good, we will simply continue our example and create a class that implements ISessionConfigurationEvents and override the configuration of the mapping between the Customer property and its column:

 1: public class PersistenceConfiguration : ISessionConfigurationEvents {
 2:  
 3:     public void Created(FluentConfiguration cfg, AutoPersistenceModel defaultModel) {
 4:         defaultModel.Override(mapping => mapping.References(x => x.Customer, "CustomerId"));
 5:     }
 6:  
 7:     public void Building(Configuration cfg) { }
 8:     public void Prepared(FluentConfiguration cfg) { }
 9:     public void Finished(Configuration cfg) {}
 10:     public void ComputingHash(Hash hash) {}
 11: }

With this class in place, when Orchard creates mappings, it will find this class and map the Customer property on our Product class to a column named "CustomerId", instead of "Customer_Id".
Notice that we only implemented "Created", which provides us access with to the AutoPersistenceModel, which in turn enables us to override the mapping between properties and column names. Outright awesomeness.

Although this is a trivial example, knowing this little gem should at least help you get started in customizing your entity / db mapping and taking full advantage of what NHibernate has to offer.


ISessionConfigurationEvents exposes NHibernate objects, so I highly encourage reading Fluent NHibernate's documentation to find out all there is to know about mapping and gain complete control over your entity mapping.

2 comments

  • ... Posted by Igor Minakov Posted 11/11/2014 07:25 AM

    There are some project on Github with example, how to use more NHibernate features in Orchard.CMS. I used ISessionConfigurationEvents in FluentNHibernate mapping and ISessionHolder object for repository access via ICriteria or HQL. Project located here: OrchardNH github

  • ... Posted by Sam Posted 02/07/2015 12:35 AM

    Good stuff, I've been looking for a while for a way to extend and use the awesome features NHibernate offers and this gives me exactly what I need. Glad to see this was implemented in one of the more recent versions. This blog posts are very helpful, keep up the good work!

Leave a comment