NHibernate midstream

by MikeHogg 7. March 2013 14:30

NHibernate is an active ORM product, and has been one for some time.   Coming to it as I have, at version 3 can be difficult to digest, especially as you are soaking up new syntax, even if the patterns are not unfamiliar.  Especially since there are many options and support libraries to choose from, and the core library has changed so much from version one to two and again to three. 

So just blindly googling lines of code and quickstarts can easily get you in lots of dark woods unless you pay attention to the dates of web posts, and understand how NH evolved over time. 

First, the patterns.  NH works from a Repository pattern by using db sessions.  Inject the NH SessionFactory into your repositories and then your Repos can run all the NH Query goodness instead of you writing a whole bunch of db layer stuff.  In its simplest form, you can use an NHHelper class to statically create a SessionFactory inside your Repo, or you can use Construction Injection and let your Injection Library do that for you automatically, like here with Ninject

public class NHibernateSessionFactory
    {
        public ISessionFactory GetSessionFactory()
        { //later }
        public class NHibernateSessionFactoryProvider : Ninject.Activation.Provider<ISessionFactory>
        {
            protected override ISessionFactory CreateInstance(Ninject.Activation.IContext context)
            {
                var sessionFactory = new NHibernateSessionFactory();
                return sessionFactory.GetSessionFactory();
            }
        }

 

then your Repo looks something like this:

 

 public class Repository<T> :  IRepository<T> where T:class
    {
        private readonly ISession session;
 
        public Repository(ISession s)
        {
            session = s;
        }
 

So GetSessionFactory is where all the NH code is config'd.

Here are your options for configuring...

Originally, everything was XML and config based, so you would have the base NH.config() in your code, and your web.config would have a section with the required and optional properties in it.

 

 
public ISessionFactory GetSessionFactory()
        {
            var config = new Configuration();
            config.BuildSessionFactory();
 

 

Then your web.config would have all the hookup:

 

 <configSections>
    <section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectio
nHandler, NHibernate" />
</configSections>
  <connectionStrings>
    <add name="CONN" connectionString="blahblah" providerName="System.Data.SqlClient" />
  </connectionStrings>
  <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
    <session-factory>
      <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
      <property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
      <property name="dialect">NHibernate.Dialect.MsSql2008Dialect</property>
      <property name="connection.connection_string_name">CONN</property>
      <property name="show_sql">true</property>
    </session-factory>
  </hibernate-configuration>
 

Then came Fluent, and config was moved into code. so, if you use that library, you no longer need the web.config and you can just use this in your SessionFactory...

 

        
public ISessionFactory GetSessionFactory()
        {
            // fluent style config.  We use both to apply loquacious mapping by code to old style config, and then create FluentConfiguration with it
            FluentNHibernate.Cfg.FluentConfiguration fluentConfiguration = FluentNHibernate.Cfg.Fluently.Configure()
                                                   .Database(FluentNHibernate.Cfg.Db.MsSqlConfiguration.MsSql2008.ShowSql()
                                                    .ConnectionString(c => c.FromConnectionStringWithKey("CONN")))
                                                    // only need one From Assembly to point to all the maps These are for Fluent MAPS inherting from ClassMap
                                                   .Mappings(m => m.FluentMappings.AddFromAssemblyOf<Maps.EventMap>())                
                                                   .ExposeConfiguration(cfg => cfg.SetProperty("adonet.batch_size", "20"))
                                                   .ExposeConfiguration(c => c.SetProperty("generate_statistics", "true"));                                       
                                                    //.ExposeConfiguration(BuildSchema) 
 
            return fluentConfiguration.BuildSessionFactory();

In addition to config, when setting up your NH Environment, you need to init your maps.  If you already have database tables, the first order of business is to get NMG, NHibernateMappingGenerator.  This reads the db and outputs entity classfiles and classmaps and is a huge help.  Note in the screenshot below, in the upper right hand side of the preferences, how you can choose which format of Mapping files you want.

 

Originally, these would be XML files, (convention) named something.hbm.xml.  We no longer need to do that.  You see also FluentNHibernate and the new Loquacious format.  FNH uses ClassMap parent class, and I believe that might be the convention that allows AddAssemblyFromMaps<>() to work.  Loquacious uses ClassMapping parent, and I believe offers more flexibility in filtering types from Assembly to map.

With NH 3 the project merged the Loquacious (Mapping By Code) codebase that was previously a library, but it is not very mature so a lot of posts I have seen are where it is being used for simpler project requirements or in tandem with a large base of existing Fluent mappings.  I could not figure out a way to add MBC maps to FluentMappings, so in case you ever want to do that here is code to initialize and map both ways, using a base NH.Config with web.config for MBC maps and building your FluentConfig with that base, adding FluentMaps in that process...

 
public ISessionFactory GetSessionFactory()
        {
            
            // old style config 
            var mm = new NHibernate.Mapping.ByCode.ModelMapper();
            Type[] mappingTypes = typeof(Maps.EventMap).Assembly.GetExportedTypes().Where(t => t.Name.EndsWith("Map")).ToArray();
            mm.AddMappings(mappingTypes);
 
            var config = new Configuration(); 
            config.AddMapping(mm.CompileMappingForAllExplicitlyAddedEntities());
 
 
            // fluent style config.  We use both to apply loquacious mapping by code to old style config, and then create FluentConfiguration with it
            FluentNHibernate.Cfg.FluentConfiguration fluentConfiguration = FluentNHibernate.Cfg.Fluently.Configure(config)
                                                   .Database(FluentNHibernate.Cfg.Db.MsSqlConfiguration.MsSql2008.ShowSql()
                                                    .ConnectionString(c => c.FromConnectionStringWithKey("CONN")))
                                                    // only need one From Assembly to point to all the maps These are for Fluent MAPS inherting from ClassMap
                                                   .Mappings(m => m.FluentMappings.AddFromAssemblyOf<Maps.EventMap>())                
                                                   .ExposeConfiguration(cfg => cfg.SetProperty("adonet.batch_size", "20"))
                                                   .ExposeConfiguration(c => c.SetProperty("generate_statistics", "true"));                                       
                                                    //.ExposeConfiguration(BuildSchema) 
 
            return fluentConfiguration.BuildSessionFactory();
        }

Add comment

biuquote
  • Comment
  • Preview
Loading

About Mike Hogg

Mike Hogg is a c# developer in Brooklyn.

More Here

Favorite Books

This book had the most influence on my coding style. It drastically changed the way I write code and turned me on to test driven development even if I don't always use it. It made me write clearer, functional-style code using more principles such as DRY, encapsulation, single responsibility, and more. amazon.com

This book opened my eyes to a methodical and systematic approach to upgrading legacy codebases step by step. Incrementally transforming code blocks into testable code before making improvements. amazon.com

More Here