NHibernate Cheatsheet

Tags: NHibernate, cheatsheet, classmapping, map by code, fluentnhibernate

NHibernate Cheat Sheet

NHibernate Logo

Cheatsheet for NHibernate using code first mapping.

This cheat sheet uses Sql Server and C#-sqlite for the database, but it's easy to change to others such as Oracle, MySql, etc.

Table of contents

Packages used

PM> Install-Package NHibernate

PM> Install-Package Community.CsharpSqlite.SQLiteClient

Example database connection manager

This connection manager will scan for all Mappings in the executing assembly.

an alternative to Assembly.GetExecutingAssembly() would be typeof(PersonMap).Assembly which would find other maps in the same assembly as PersonMap.

To use it:

var db = new Database(); or using(var db = new SqliteInMemoryDatabase())

// First time only (normal sql) or every time for sqlite in memory database
db.ReplaceSchema();

using (var session = db.OpenSession()) 
{
}


public interface IDatabase
    {
        ISessionFactory SessionFactory { get; }
        ISession OpenSession();
        void ReplaceSchema();
    }

public class Database : IDatabase
    {
        private ISessionFactory _sessionFactory;

        public ISessionFactory SessionFactory
        {
            get { return _sessionFactory ?? (_sessionFactory = BuildSessionFactory()); }
        }

        protected virtual void ConfigureDatabase(IDbIntegrationConfigurationProperties db)
        {
            db.Driver<SqlClientDriver>();
            db.Dialect<MsSql2012Dialect>();
            db.ConnectionStringName = "Database";
        }

        protected Configuration GetConfiguration()
        {
            var configuration = new Configuration();

            configuration.DataBaseIntegration(ConfigureDatabase);
            var mapper = new ModelMapper();
                mapper.AddMappings(Assembly.GetExecutingAssembly().GetExportedTypes());

            var mapping = mapper.CompileMappingForAllExplicitlyAddedEntities();

            configuration.AddMapping(mapping);

            return configuration;
        }

        private ISessionFactory BuildSessionFactory()
        {
            var configuration = GetConfiguration();   

            return configuration.BuildSessionFactory();
        }

        public virtual ISession OpenSession()
        {
            return SessionFactory.OpenSession();
        }

        public virtual void ReplaceSchema()
        {
            var cfg = GetConfiguration();
            var exporter = new SchemaExport(cfg);

            exporter.Create(false, true);
        }
    }

Override for a Sqlite InMemory database

This needs to keep the connection open to avoid a new beeng created for every request which would be useless since a ReplaceSchema() call followed by OpenSession() would still return an empty database.

public class SqliteInMemoryDatabase : Database, IDisposable
    {
        private SqliteConnection _connection = new SqliteConnection("Data Source=:memory:;Version=3;New=True;");
        private bool _disposed;

        public SqliteInMemoryDatabase()
        {
            _connection.Open();
        }

        protected override void ConfigureDatabase(IDbIntegrationConfigurationProperties db)
        {
            db.Driver<CsharpSqliteDriver>();
            db.Dialect<SQLiteDialect>();
        }

        public override ISession OpenSession()
        {
            if (_disposed) throw new ObjectDisposedException(GetType().Name);

            return SessionFactory.OpenSession(_connection);
        }

        public override void ReplaceSchema()
        {
            if (_disposed) throw new ObjectDisposedException(GetType().Name);

            var cfg = GetConfiguration();
            var exporter = new SchemaExport(cfg);

            exporter.Create(ExecuteSql, false);
        }

        private void ExecuteSql(string sql)
        {
            var cmd = _connection.CreateCommand();

            cmd.CommandText = sql;
            cmd.ExecuteNonQuery();

            cmd.Dispose();
        }

        public virtual void Dispose(bool disposing)
        {
            if (_disposed)
                return;

            if (disposing)
            {
                if (_connection != null)
                    _connection.Dispose();

                _connection = null;

            }

            _disposed = true;
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    }

Mapping by Code

Sample Person class (Simple)

This class has only properties and no references or complex types.

 public enum EducationLevel
    {
        Highschool,
        Bachelor,
        Master,
        Phd
    }

    public class Person
    {
        public virtual int Id { get; set; }
        public virtual string Name { get; set; }
        public virtual DateTime? BithDate { get; set; }
        public virtual EducationLevel EducationLevel { get; set; }
        public virtual int Paygrade { get; set; }
        public virtual decimal? HourlyRate { get; set; }
        public virtual long PassportNumber { get; set; }
        public virtual float? JustATest { get; set; }
    }

...and a corresponding map class

public class PersonMap : ClassMapping<Person>
    {
        public PersonMap()
        {
            Id(person => person.Id, a => a.Generator(Generators.Assigned));
            Table("People")
            Property(person => person.Name);
            Property(person => person.BithDate);
            Property(person => person.EducationLevel);
            Property(person => person.Paygrade);
            Property(person => person.HourlyRate);
            Property(person => person.PassportNumber);
            Property(person => person.JustATest);
        }
    }

This is the generated NHibernate XML mapping:

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" namespace="MapTester.MapFunctions.Id" assembly="MapTester, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" xmlns="urn:nhibernate-mapping-2.2">
  <class name="Person" table="People">
    <id name="Id" type="Int32">
      <generator class="identity" />
    </id>
    <property name="Name" />
    <property name="BithDate" />
    <property name="EducationLevel" />
    <property name="Paygrade" />
    <property name="HourlyRate" />
    <property name="PassportNumber" />
    <property name="JustATest" />
  </class>
</hibernate-mapping>

The MS SQL Server (2012) create statement for this class:

 create table People (
     Id INT IDENTITY NOT NULL,
    Name NVARCHAR(255) null,
    BithDate DATETIME null,
    EducationLevel INT null,
    Paygrade INT null,
    HourlyRate DECIMAL(19,5) null,
    PassportNumber BIGINT null,
    JustATest REAL null,
    primary key (Id)
 )

The Oracle (10g) create statement for this class:

create table People (
    Id NUMBER(10,0) not null,
   Name NVARCHAR2(255),
   BithDate TIMESTAMP(4),
   EducationLevel NUMBER(10,0),
   Paygrade NUMBER(10,0),
   HourlyRate NUMBER(19,5),
   PassportNumber NUMBER(20,0),
   JustATest FLOAT(24),
   primary key (Id)
)

create sequence hibernate_sequence

Add a new Person

var p = new Person
    {
        Name = "Bill Gates",
        EducationLevel = EducationLevel.Highschool,
        HourlyRate = 1000,
        Paygrade = 10
    };

    using (var session = db.OpenSession())
        {
            session.Save(p);
        }

On MS Sql Server 2012:

INSERT
INTO
    People
    (Name, BithDate, EducationLevel, Paygrade, HourlyRate, PassportNumber, JustATest)
VALUES
    (@p0, @p1, @p2, @p3, @p4, @p5, @p6);
select
    SCOPE_IDENTITY();

@p0 = 'Bill Gates' [Type: String (4000)],
@p1 = NULL [Type: DateTime (0)],
@p2 = 0 [Type: Int32 (0)],
@p3 = 10 [Type: Int32 (0)],
@p4 = 1000 [Type: Decimal (0)],
@p5 = 0 [Type: Int64 (0)],
@p6 = NULL [Type: Single (0)]

On Oracle 10g:

INSERT
INTO
    People
    (Id, Name, BithDate, EducationLevel, Paygrade, HourlyRate, PassportNumber, JustATest)
VALUES
    (hibernate_sequence.nextval, :p0, :p1, :p2, :p3, :p4, :p5, :p6) returning Id
into
    :nhIdOutParam;

:p0 = 'Bill Gates' [Type: String (0)], :p1 = NULL [Type: DateTime (0)], :p2 = 0 [Type: Int32 (0)], :p3 = 10 [Type: Int32 (0)], :p4 = 1000 [Type: Decimal (0)], :p5 = 0 [Type: Int64 (0)], :p6 = NULL [Type: Single (0)], :nhIdOutParam = NULL [Type: Int32 (0)]

No Comments

Add a Comment