Another NHibernate 101?

17 בדצמבר 2010

4 תגובות

Yesterday at the Alt.Net tools night I gave a short talk about how to get started with NHibernate. “Getting started” means we’re talking about Greenfield project, heavily favoring Conventions (and automappings) over Configuration, and not having to mess with existing, untouchable codebase or DB schema.

The weird part was sitting there, after having used NH, a 5 year-old project, for about a week, and across the table sat Ayende, one of the main contributors to NH (and author of NHProf). Most of the time he wasn’t throwing heavy items at me, so I called that session a success.

 

The goal of the demo was to make this integration test pass:

[Test]

public void SaveAndLoadEntity_AssertSavedValueIsSame()

{

    // Arrange

    var instance = new Saver();

    var entity = new MyEntity();

    entity.Val = 34;

    instance.Save(entity);

 

    // Act

    var retVal = instance.Load();

 

    // Assert

    Assert.AreEqual(34, retVal.Val);

}

(The test was created in exactly three seconds using QuickUnit. Try it out.)

I won’t re-run all the “run test, fix error” iterations here, just review the concepts and show the outcome.

DB config Issues:

  • We want the test to pass as easily as possible, have no side effects, and require minimal setup costs – so we’ve used SQLite in-memory DB, using System.Data.Sqlite (free). The single DLL is the ADO.NET adapter AND the actual DB.
  • Standard configuration of in-memory SQLite kills the DB after every session closes, so we needed a different connection string.
  • Since we’re creating a new DB every test – we need to create the DB schema every time, using the SchemaExport tool. (More)
  • Make sure the reference to System.Data.Sqlite has the “Copy local = true” flag (as it is not a completely managed dll, and does not default to true).
  • Also watch out for 64bit issues and FW4.0 issues with System.Data.Sqlite.

Mapping issues:

  • We desperately want to use FluentNHibernates’ automapping, so we provide an assembly to scan for entities, and a minimal adaptation of the DefaultAutomappingConfiguration object (I’ve chose to use attributes to mark persisted types. Inheritance can do just the same).
  • Every entity needs a primary key in RDBMS. So we’ve added the Id property on MyEntity.
  • NH wants to provide you with lazy loading for your data, so it needs to override your properties (creating dynamic proxies at runtime), so you need to make them virtual (and add a reference to the NHibernate.ByteCode.Castle assembly)

NHibernate usage issues:

  • An ISession object is lightweight (and can be thought of as “Cache Scope”). Create one when it’s a logical thing to do.
  • An ISessionFactory object is VERY heavyweight. Create and save it.
  • Always open (and commit) a transaction (saving OR loading).
  • The three main query mechanisms for NH are HQL, ICriteria and Linq2NH. I find it silly that in 2010 I won’t use linq to express my queries, so (for NH 2.1.2 only) you need to add the NHibernate.Linq.dll. NH v3.0 incorporated that syntax in the core.

Test issues:

  • Make sure you add the required references above to the tests project, so that NHibernate can, at runtime, use all those assemblies required.
   1: using System;

   2: using FluentNHibernate.Automapping;

   3: using FluentNHibernate.Cfg;

   4: using FluentNHibernate.Cfg.Db;

   5: using NHibernate;

   6: using System.Linq;

   7: using NHibernate.Cfg;

   8: using NHibernate.Linq;

   9: using NHibernate.Tool.hbm2ddl;

  10:  

  11: namespace NHStart

  12: {

  13:     public class Saver

  14:     {

  15:         public void Save(MyEntity entity)

  16:         {

  17:             using (var session = GetSession())

  18:             using(var tx = session.BeginTransaction())

  19:             {

  20:                 session.Save(entity);

  21:  

  22:                 tx.Commit();

  23:             }

  24:         }

  25:  

  26:         private ISession GetSession()

  27:         {

  28:             var sessionFactory = Factory;

  29:             return sessionFactory.OpenSession();

  30:         }

  31:  

  32:         protected ISessionFactory _Factory;

  33:         protected ISessionFactory Factory

  34:         {

  35:             get

  36:             {

  37:                 if (_Factory == null)

  38:                 {

  39:                     Configuration config = null;

  40:  

  41:                     _Factory =

  42:                         Fluently

  43:                             .Configure()

  44:                             .Database(SQLiteConfiguration.Standard.ConnectionString(x => x.Is("Data Source=:memory:;Version=3;New=True;Pooling=True;Max Pool Size=1")))

  45:                             .Mappings(x=>x.AutoMappings.Add(AutoMap

  46:                             .Assemblies(new MyAutomappingConfiguration(), typeof(MyEntity).Assembly)))

  47:                             .ExposeConfiguration(x=>config = x)

  48:                             .BuildSessionFactory();

  49:  

  50:                     using (var session = _Factory.OpenSession())

  51:                     {

  52:                         new SchemaExport(config).Execute(false, true, false, session.Connection, Console.Out);

  53:                     }

  54:  

  55:                 }

  56:                 return _Factory;

  57:             }

  58:         }

  59:  

  60:         public MyEntity Load()

  61:         {

  62:             using (var session = GetSession())

  63:             using (var tx = session.BeginTransaction())

  64:             {

  65:                 var item = session

  66:                     .Linq<MyEntity>()

  67:                     .First();

  68:  

  69:                 tx.Commit();

  70:  

  71:                 return item;

  72:             }

  73:         }

  74:     }

  75:  

  76:     public class MyAutomappingConfiguration : DefaultAutomappingConfiguration

  77:     {

  78:         public override bool ShouldMap(Type type)

  79:         {

  80:             return type.GetCustomAttributes(true).OfType<PersistedAttribute>().Any();

  81:         }

  82:     }

  83:  

  84:     [Persisted]

  85:     public class MyEntity

  86:     {

  87:         public virtual int Id { get; set; }

  88:         public virtual int Val { get; set; }

  89:         public virtual string Val2 { get; set; }

  90:     }

  91:  

  92:     public class PersistedAttribute : Attribute {}

  93: }

I’ve even added line count to show how 93 lines of code can create a (simple) persistence layer. It was fun.

הוסף תגובה
facebook linkedin twitter email

כתיבת תגובה

האימייל לא יוצג באתר. שדות החובה מסומנים *

4 תגובות

  1. ripper23419 בדצמבר 2010 ב 13:54

    Great post. I wasn't able to attend this talk, but I really like the idea.

    Too many tools and frameworks out there don't have an easy "Hello World" guide, and are relatively hard to learn for the first time. Good work on providing one.

    הגב
  2. Ariel20 בדצמבר 2010 ב 14:14

    Thanks, Ron. That was the motivation for the talk, too. 🙂

    הגב
  3. KeleraCeN9 במאי 2013 ב 1:37

    Timex Mysportsshop.com [url=http://www.replica-watch.org]fake watches[/url] Purchasing the right bleach sword is considered to be the major and important task. In this article we will discuss that how person purchase the bleach sword online. By using the facility of internet, person will find the wide variety of bleach sword. There are various types of bleach swords are available along with the multiple shapes, different sizes and lengths. The second one is the line of Panerai Luminor Marina replica. of this line own wonderful functions of water resistance. Special devices make these fake watches work well in the deep ocean, which made a patent in the last century. [url=http://www.ukwatcheshop.co.uk]cheap replica watches[/url] Luxury Rolex is an exquisite, well-known brand that defines status, standard and quality sense of style for people. But unfortunately, this ideal symbol of perfection is quite an expensive deal for many. Due its high price value, many people cannot afford it, but this does not mean that they cannot have the lucrative chance of flaunting it ever on their wrists. With the arrival of replicas of Rolex watches, many people have got to materialize their dream of owning a Rolex and showing it off among their friends boastfully. CaseThickness: 18.0 mm

    הגב
  4. PepLypediop20 ביוני 2013 ב 1:47

    844;園ではマーケットが開かれてるんですがものすごーくやる気がないマーケットで、
    プラダ財布 2452;オハザード周年記念ロゴ」と「BRIEFING」の記念すべきコラボタグが。さらに&#20184

    2428;ています。そのクラシカルでモダンなデザインは日本においても大人の女性中心&#12395 ダコタ バッグ ;pに行くのもいいな。いっそのこと、旅行して、旅先で買ってもいいな、なんてね&#122
    マイモスティック 367円 セロリ漬け 367円 梅たたききゅうり 367円 お新香盛&
    トリーバーチ店舗 #28857;商品によって違いますのでご了承、ご理解お願いいたしますサイズは実寸で計測&#123

    הגב