Introduction
For people that don't know what NHibernate is."NHibernate is a mature, open source object-relational mapper for the .NET framework.
It's actively developed, fully featured and used in thousands of successful projects." Source
This "NHibernate: Getting started guide" post will explain the basics and how to use NHibernate with:
Used techniques and design patterns
Source code
You can find the source code of this post on: https://github.com/gergroen/NHibernate-getting-started-guidePost summary
Installing NuGet
First install the NuGet Package Manager if it is not installed.Download NHibernate, SQLite and NUnit with NuGet
NHibernateNow you can open the "NuGet Package Manager" by clicking with the right mouse button on your project and choose "Manage NuGet Packges..."
Search for "NHibernate" and click on Install.
Two references are added to your project "NHibernate" and "Iesi.Collections".
"Iesi.Collections" is added because "NHibernate" has a dependency on "Iesi.Collections".
SQLite
Start the "NuGet Package Manager" again, search for "System.Data.SQLite.Core" and click on Install.
NUnit
Start the "NuGet Package Manager" again, search for "NUnit" and click on install.
To run the unit tests in Visual Studio you can use tools like ReSharper or Testdriven.net.
If you don't need to run the unit tests in Visual Studio you can download the "NUnit.Runners" via NuGet.
"NUnit.Runners" allows you to run the unit tests through the command line or a gui.
Create and test a domain model
After downloading all the packages we can start with the domain model.First we create a simple "Person" domain model with the properties "Id", "FirstName" and a "LastName".
Also we include the method "GetFullName();".
using System; namespace NHibernate.GettingStarted.Model { public class Person { public virtual Guid Id { get; set; } public virtual string FirstName { get; set; } public virtual string LastName { get; set; } public virtual string GetFullName() { return string.Format("{0} {1}", FirstName, LastName); } } }After creating the domain model we create a test class to test the domain model.
We're using "NUnit" to unit test our domain model.
using NHibernate.GettingStarted.Model; using NUnit.Framework; namespace NHibernate.GettingStarted.Test { [TestFixture] public class PersonTest { [Test] public void GetFullNameTest() { var person = new Person { FirstName = "Test", LastName = "Kees" }; Assert.AreEqual("Test", person.FirstName); Assert.AreEqual("Kees", person.LastName); Assert.AreEqual("Test Kees", person.GetFullName()); } } }
Create and test NHibernate mapping by code
You can create the NHibernate mapping for your domain models in different ways. In this example i use NHibernate mapping by code.using NHibernate.GettingStarted.Model; using NHibernate.Mapping.ByCode; using NHibernate.Mapping.ByCode.Conformist; namespace NHibernate.GettingStarted.Dao { public class PersonMap : ClassMapping<Person> { public PersonMap() { Id(x => x.Id, m => m.Generator(Generators.GuidComb)); Property(x => x.FirstName); Property(x => x.LastName); } } }To see the result of the mapping we create this unit test.
The test output is the traditional xml NHibernate mapping.
using System; using System.Xml.Serialization; using NHibernate.GettingStarted.Dao; using NHibernate.Mapping.ByCode; using NUnit.Framework; namespace NHibernate.GettingStarted.Test { [TestFixture] public class PersonMapTest { [Test] public void CanGenerateXmlMapping() { var mapper = new ModelMapper(); mapper.AddMapping<PersonMap>(); var mapping = mapper.CompileMappingForAllExplicitlyAddedEntities(); var xmlSerializer = new XmlSerializer(mapping.GetType()); xmlSerializer.Serialize(Console.Out, mapping); } } }
Test output:
<?xml version="1.0" encoding="Windows-1252"?> <hibernate-mapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" namespace="NHibernate.GettingStarted.Model" assembly="NHibernate.GettingStarted" xmlns="urn:nhibernate-mapping-2.2"> <class name="Person"> <id name="Id" type="Guid"> <generator class="guid.comb" /> </id> <property name="FirstName" /> <property name="LastName" /> </class> </hibernate-mapping>
Configure NHibernate for SQLite
To configure NHibernate we need to create a "nhibernate.cfg.xml" file and set the Copy to Output Directory to "Copy Always".<?xml version="1.0" encoding="UTF-8"?> <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2"> <session-factory name="NHibernate.Test"> <property name="connection.driver_class">NHibernate.Driver.SQLite20Driver</property> <property name="connection.connection_string">Data Source=test.db;Version=3;New=True</property> <property name="dialect">NHibernate.Dialect.SQLiteDialect</property> <property name="show_sql">true</property> </session-factory> </hibernate-configuration>
Create and test a NHibernate helper
Now we create a NHibernate helper class to load the configuration and the mapping to create sessions.using System.Collections.Generic; using NHibernate.Cfg; using NHibernate.Cfg.MappingSchema; using NHibernate.Mapping.ByCode; namespace NHibernate.GettingStarted.Dao { public static class NHibernateHelper { private static ISessionFactory _sessionFactory; private static Configuration _configuration; private static HbmMapping _mapping; public static ISession OpenSession() { //Open and return the nhibernate session return SessionFactory.OpenSession(); } public static ISessionFactory SessionFactory { get { if (_sessionFactory == null) { //Create the session factory _sessionFactory = Configuration.BuildSessionFactory(); } return _sessionFactory; } } public static Configuration Configuration { get { if (_configuration == null) { //Create the nhibernate configuration _configuration = CreateConfiguration(); } return _configuration; } } public static HbmMapping Mapping { get { if (_mapping == null) { //Create the mapping _mapping = CreateMapping(); } return _mapping; } } private static Configuration CreateConfiguration() { var configuration = new Configuration(); //Loads properties from hibernate.cfg.xml configuration.Configure(); //Loads nhibernate mappings configuration.AddDeserializedMapping(Mapping, null); return configuration; } private static HbmMapping CreateMapping() { var mapper = new ModelMapper(); //Add the person mapping to the model mapper mapper.AddMappings(new List<System.Type> { typeof(PersonMap) }); //Create and return a HbmMapping of the model mapping in code return mapper.CompileMappingForAllExplicitlyAddedEntities(); } } }
Database schema test
using System; using NHibernate.Tool.hbm2ddl; using NUnit.Framework; namespace NHibernate.GettingStarted.Dao.Test { [TestFixture] public class SchemaTest { [Test] public void CanGenerateSchema() { var schemaUpdate = new SchemaUpdate(NHibernateHelper.Configuration); schemaUpdate.Execute(Console.WriteLine, true); } } }Test output:
create table Person ( Id UNIQUEIDENTIFIER not null, FirstName TEXT, LastName TEXT, primary key (Id) )
Create and test a Repository
With the NHibernateHelper we can create a person repository to save, get, update and delete persons to the SQLite database. First we create a repository interface.using System; namespace NHibernate.GettingStarted.Model { public interface IPersonRepository { /// <summary> /// Get person entity by id /// </summary> /// <param name="id">id</param> /// <returns>person</returns> Person Get(Guid id); /// <summary> /// Save person entity /// </summary> /// <param name="person">person</param> void Save(Person person); /// <summary> /// Update person entity /// </summary> /// <param name="person">person</param> void Update(Person person); /// <summary> /// Delete person entity /// </summary> /// <param name="person">person</param> void Delete(Person person); /// <summary> /// Row count person in db /// </summary> /// <returns>number of rows</returns> long RowCount(); } }Repository class
using System; using NHibernate.GettingStarted.Model; namespace NHibernate.GettingStarted.Dao { public class NHibernatePersonRepository : IPersonRepository { public void Save(Person person) { using (ISession session = NHibernateHelper.OpenSession()) using (ITransaction transaction = session.BeginTransaction()) { session.Save(person); transaction.Commit(); } } public Person Get(Guid id) { using (ISession session = NHibernateHelper.OpenSession()) return session.Get<Person>(id); } public void Update(Person person) { using (ISession session = NHibernateHelper.OpenSession()) using (ITransaction transaction = session.BeginTransaction()) { session.Update(person); transaction.Commit(); } } public void Delete(Person person) { using (ISession session = NHibernateHelper.OpenSession()) using (ITransaction transaction = session.BeginTransaction()) { session.Delete(person); transaction.Commit(); } } public long RowCount() { using (ISession session = NHibernateHelper.OpenSession()) { return session.QueryOver<Person>().RowCountInt64(); } } } }Repository test
using System.IO; using NHibernate.GettingStarted.Model; using NHibernate.Tool.hbm2ddl; using NUnit.Framework; namespace NHibernate.GettingStarted.Dao.Test { [TestFixture] public class NHibernatePersonRepositoryTest { private IPersonRepository _personRepo; [SetUp] public void CreateSchema() { DeleteDatabaseIfExists(); var schemaUpdate = new SchemaUpdate(NHibernateHelper.Configuration); schemaUpdate.Execute(false, true); _personRepo = new NHibernatePersonRepository(); } [Test] public void CanSavePerson() { _personRepo.Save(new Person()); Assert.AreEqual(1, _personRepo.RowCount()); } [Test] public void CanGetPerson() { var person = new Person(); _personRepo.Save(person); Assert.AreEqual(1, _personRepo.RowCount()); person = _personRepo.Get(person.Id); Assert.IsNotNull(person); } [Test] public void CanUpdatePerson() { var person = new Person(); _personRepo.Save(person); Assert.AreEqual(1, _personRepo.RowCount()); person = _personRepo.Get(person.Id); person.FirstName = "Test"; _personRepo.Update(person); Assert.AreEqual(1, _personRepo.RowCount()); Assert.AreEqual("Test", _personRepo.Get(person.Id).FirstName); } [Test] public void CanDeletePerson() { var person = new Person(); _personRepo.Save(person); Assert.AreEqual(1, _personRepo.RowCount()); _personRepo.Delete(person); Assert.AreEqual(0, _personRepo.RowCount()); } [TearDown] public void DeleteDatabaseIfExists() { if (File.Exists("test.db")) File.Delete("test.db"); } } }
Useful links:
Source codeNHibernate documentation
Really nice guide!
ReplyDeleteHi Gerard
ReplyDeleteGreat guide! Thanks so much for putting it all together.
Minor typo: GetFullName() has an incorrect brace before it, instead of after.
Something that I did wonder about is my VS2010 wasn't happy with a recognising some of the types. For example in CanGenerateXmlMapping, I needed to change
"mapper.AddMapping()" to
"mapper.AddMapping()".
Similarly in NHibernateHelper.CreateMapping() I needed to change
"mapper.AddMappings(new List { typeof(PersonMap) });" to
"mapper.AddMappings(new List { typeof(PersonMap) });".
Cheers
Carlos
Sorry...
ReplyDelete"mapper.AddMapping()" to
"mapper.AddMapping<PersonMap>()".
Similarly in NHibernateHelper.CreateMapping() I needed to change
"mapper.AddMappings(new List { typeof(PersonMap) });" to
"mapper.AddMappings(new List<System.Type> { typeof(PersonMap) });".
Cheers
Carlos
Thanks for the feedback Carlos. I've updated the post.
DeleteThanks! It made it very clear!
ReplyDeleteI have a problem tho.
I was trying to extend the functionality and wanted to add an external data where I have some more complex models but I got the following exception:
The type or namespace name 'External' could not be found (are you missing a using directive or an assembly reference?)
which means in my code:
using External.Infrastructure.Medicine;
using System;
namespace NHibernate.GettingStarted.Model
{
public interface IMedicineRepository
{
Medicine Get(Guid id);
...
does anyone have an idea how can I integrate this external project to my SQLite test project?
Thanks!
John
great
ReplyDeleteI assume this is a typo: using nhibernate.cfg.xml caused an exception, because the code was searching for hibernate.cfg.xml. Naming that file hibernate.cfg.xml fixes the issue.
ReplyDelete