NHibernate Built-in Code Based Mapping instead of using Fluent NHibernate

This is just a quick post to show how to use the new NHibernate built-in mapping instead of using Fluent NHibernate. It looks like the Fluent Hibernate is not compatible with the latest version of NHibernate that is available in NuGet. Although I am a fan of Fluent NHibernate but I am not sure about it’s future as the core NHibernate now has this similar kind of code based mapping instead of those boring xml files. I will show you the Fluent NHibernate Mapping first then the new mapping. This is extracted from one of my pet project that I am using to play with jQuery Mobile and Spine. Lets say I have a basic User object which was mapped in Fluent NHibernate like the following:

Fluent NHibernate

              public sealed class UserMap : ClassMap<User>
              {
                  public UserMap()
                  {
                      Id(u => u.Id);
              
                      Map(u => u.UserName).Not
                                          .Nullable()
                                          .Length(32)
                                          .UniqueKey("UQ_User_UserName");
                      Map(u => u.Password).Not.Nullable().Length(64);
                      Map(u => u.Salt).Not.Nullable().Length(64);
                      Map(u => u.Email).Not
                                       .Nullable()
                                       .Length(256)
                                       .UniqueKey("UQ_User_Email");
                      Map(u => u.Locked);
                      Map(u => u.CreatedAt);
              
                      HasMany(u => u.Tasks);
                      HasMany(u => u.ToDoLists);
                  }
              }
              

Becomes

NHibernate Core Mapping

              public class UserMap : IClassMap
              {
                  public void Map(ModelMapper mapper)
                  {
                      mapper.Class<User>(m =>
                      {
                          m.Id(u => u.Id);
              
                          m.Property(
                              u => u.UserName, c =>
                          {
                              c.NotNullable(true);
                              c.Length(32);
                              c.UniqueKey("UQ_User_UserName");
                          });
              
                          m.Property(u => u.Password, c =>
                          {
                              c.NotNullable(true);
                              c.Length(64);
                          });
              
                          m.Property(u => u.Salt, c =>
                          {
                              c.NotNullable(true);
                              c.Length(64);
                          });
              
                          m.Property(u => u.Email, c =>
                          {
                              c.NotNullable(true);
                              c.Length(256);
                              c.UniqueKey("UQ_User_Email");
                          });
              
                          m.Property(u => u.Locked);
                          m.Property(u => u.CreatedAt);
                      });
                  }
              }
              

Lets take another example, this time the Task object:

Fluent NHibernate

              public sealed class TaskMap : ClassMap<Task>
              {
                  public TaskMap()
                  {
                      Id(t => t.Id);
              
                      Map(t => t.Name).Not.Nullable().Length(256);
                      Map(t => t.Planned).Not.Nullable();
                      Map(t => t.Notes).Nullable().Length(10000);
                      Map(t => t.Estimation).Nullable();
                      Map(t => t.CreatedAt).Not
                                           .Nullable()
                                           .Index("IX_Task_UserId_CreatedAt");
              
                      Map(t => t.DueAt).Nullable()
                                       .Index("IX_Task_CompletedAt_DueAt_Archived");
              
                      Map(t => t.CompletedAt).Nullable()
                                             .Index("IX_Task_CompletedAt_DueAt_Archived");
              
                      Map(t => t.Archived).Not
                                          .Nullable()
                                          .Index("IX_Task_CompletedAt_DueAt_Archived");
              
                      References(t => t.User).Not
                                             .Nullable()
                                             .ForeignKey()
                                             .Index("IX_Task_UserId_CreatedAt");
                  }
              }
              

is changed to:


              public class TaskMap : IClassMap
              {
                  public void Map(ModelMapper mapper)
                  {
                      mapper.Class<Task>(m =>
                      {
                          m.Id(t => t.Id);
              
                          m.Property(t => t.Name, c =>
                          {
                              c.NotNullable(true);
                              c.Length(256);
                          });
              
                          m.Property(t => t.Planned);
              
                          m.Property(
                              t => t.Notes, c =>
                          {
                              c.NotNullable(false);
                              c.Length(4001);
                          });
              
                          m.Property(t => t.Estimation);
              
                          m.Property(
                              t => t.CreatedAt,
                              c => c.Index("IX_Task_UserId_CreatedAt"));
              
                          m.Property(
                              t => t.DueAt,
                              c => c.Index("IX_Task_CompletedAt_DueAt_Archived"));
              
                          m.Property(
                              t => t.CompletedAt,
                              c => c.Index("IX_Task_CompletedAt_DueAt_Archived"));
              
                          m.Property(
                              t => t.Archived,
                              c => c.Index("IX_Task_CompletedAt_DueAt_Archived"));
              
                          m.ManyToOne(
                              t => t.User, c =>
                          {
                               c.Column("UserId");
                               c.NotNullable(true);
                               c.ForeignKey("FK_Task_User");
                               c.Index("IX_Task_UserId_CreatedAt");
                          });
                      });
                  }
              }
              

Now, the most important thing building the NH SessionFactory, please note that in the above, the core NHibernate mapping the IClassMap interface does not belongs to the NHibernate, it is just a custom interface by me to use separate classes for each object mapping (If there is any built-in class in NHibernate the does the same thing, please free to post it in the comment box below). Here is the code that builds the SessionFactory.

Building Session Factory

              private static ISessionFactory BuildSessionFactory(
                  string connectionString, string providerName)
              {
                  var mapper = new ModelMapper();
              
                  mapper.AfterMapClass += (inspector, type, customizer) =>
                      customizer.Id(m => m.Generator(Generators.GuidComb));
              
                  mapper.AfterMapProperty += (inspector, member, customizer) =>
                  {
                      var memberType = member.LocalMember.GetPropertyOrFieldType();
              
                      if (memberType.IsGenericType &&
                          nullableType.IsAssignableFrom(
                          memberType.GetGenericTypeDefinition()))
                      {
                          customizer.NotNullable(false);
                      }
                      else if (!stringType.IsAssignableFrom(memberType))
                      {
                          customizer.NotNullable(true);
                      }
                  };
              
                  foreach (var map in typeof(SessionProvider).Assembly
                                              .GetTypes()
                                              .Where(t =>
                                                  t.IsClass &&
                                                  !t.IsAbstract &&
                                                  typeof(IClassMap).IsAssignableFrom(t))
                                              .Select(Activator.CreateInstance)
                                              .OfType<IClassMap>())
                  {
                      map.Map(mapper);
                  }
              
                  var cfg = new Configuration();
              
                  cfg.DataBaseIntegration(c =>
                  {
                      c.ConnectionString = connectionString;
              
                      if (providerName.Equals(
                          "System.Data.SqlServerCe.4.0",
                          StringComparison.OrdinalIgnoreCase))
                      {
                          c.Driver<SqlServerCeDriver>();
                          c.Dialect<MsSqlCe40Dialect>();
                      }
                      else
                      {
                          c.Driver<SqlClientDriver>();
                          c.Dialect<MsSql2008Dialect>();
                      }
              
                      #if DEBUG
                          c.LogSqlInConsole = true;
                          c.LogFormattedSql = true;
                      #endif
              
                      c.KeywordsAutoImport = Hbm2DDLKeyWords.AutoQuote;
                      c.SchemaAction = SchemaAutoAction.Create;
                  });
              
                  cfg.AddMapping(
                      mapper.CompileMappingForAllExplicitlyAddedEntities());
              
                  SchemaMetadataUpdater.QuoteTableAndColumns(cfg);
              
                  return cfg.BuildSessionFactory();
              }
              

There are two interesting things in the above first I am using two ModelMapper events to add my own convention, first all of table primary key generator should be the GuidComb and next based up the type I am setting up whether the column should support null or not, in Fluent NHibernate this support was available through some interfaces and lastly there check for the type of database, in development and test mode I am using SQLCompact edition and for production SQL Server.

That’s it for tonight, maybe some of the non NHibernate Mafia like me find it useful.

[Update: Hazzik is correct the Fluent NHibernate that is available in NuGet is fully compatible with the latest NHibernate.]

[Update 2: Check the gist of Sergueev in the comments section, how you can use the built-in class of NHibernate instead of creating custom interface like I did for the mapping.]

Shout it

Comments

blog comments powered by Disqus