[C#/NHibernate] 10分鐘快速體驗NHibernate
最近聽聞專案存取資料庫技術使用的是NHibernate,趕緊趁開工前Study了一下
前言
NHibernate是一套用物件操作方式存取資料庫的Framework
本文以Console專案(.Net4.5)搭配SQL Server Express 2012 做展示
實作
先自行建立一個Console專案後
Step 1 .接著加入相關的.dll一切才能開始
建議剛開始從官網下載壓縮包
本文下載的是NHibernate-3.3.1.GA-bin.zip
下載解壓後把資料夾裡Required_Bins目錄裡的Iesi.Collections.dll和NHibernate.dll加入參考
Q:為什麼不從NuGet套件安裝NHibernate呢?
A:可以是可以,但NuGet只會安裝基本用到的.dll(Iesi.Collections.dll和NHibernate.dll)
玩下去沒多久很快就會卡關,因為一開始就不知道.xml設定檔從何設定起,從官網下載的壓縮包裡面至少還有上手說明,資源較完整
然後還要再加入Lazy Loading會用到的.dll
NHibernate.LinFu
或
NHibernate.Castle
本文使用的是NHibernate.LinFu
從專案右鍵>管理NuGet套件>搜尋NHibernate.LinFu安裝
確認「參考」底下確實有「LinFu.DynamicProxy」、「NHibernate.ByteCode.LinFu」這兩個.dll
沒加入Lazy-Loading會用的.dll的話,到時候程式一執行可能發生
類型 'NHibernate.Bytecode.UnableToLoadProxyFactoryFactoryException' 的未處理例外狀況發生於 NHibernate.dll
或
An exception occurred in the persistence layer.
Step 2.加入NHibernate xml設定檔
從專案右鍵
名稱最好取名:hibernate.cfg.xml
為了要讓此xml檔可以有NHibernate相關的intellisense,從剛剛下載來的壓縮包內加入結構描述
整個設定字串也無須自己慢慢敲打
打開 NHibernate-3.3.1.GA-bin\HowInstall.txt檔
http://nhforge.org/blogs/nhibernate/archive/2008/11/09/nh2-1-0-bytecode-providers.aspx
由於本文使用的是NHibernate.LinFu
貼回hibernate.cfg.xml改一改
並確認複製到輸出目錄為「永遠複製」
Step 3. 為專案加入兩個資料夾:Domain、Mapping
Step 4.加入Entity類別定義
先建好DB Table:Persons (因為我習慣DB First)
(
 PersonID int Not NULL identity primary key,
 PersonName nvarchar(10) Not NULL default('')
)
Go新增類別,注意要加上virtual關鍵字
否則可能發生
The following types may not be used as proxies:
NHibernateTest.Domain.Persons: method get_PersonID should be 'public/protected virtual' or 'protected internal virtual'
NHibernateTest.Domain.Persons: method set_PersonID should be 'public/protected virtual' or 'protected internal virtual'
NHibernateTest.Domain.Persons: method get_PersonName should be 'public/protected virtual' or 'protected internal virtual'
NHibernateTest.Domain.Persons: method set_PersonName should be 'public/protected virtual' or 'protected internal virtual'
Step 5.在Mapping資料夾底下加入Persons類別的描述檔.xml
檔名命名規則:類別名稱.hbm.xml(hbm為Hibernate Mapping縮寫)
為了讓此份xml有NHibernate相關的intellisense,從右上角的屬性加入結構描述檔,位置在NHibernate-3.3.1.GA-bin\Required_Bins\nhibernate-mapping.xsd
此xml的建置動作和輸出目錄跟剛剛的不太一樣
xml設定字串可以參考官方文件http://nhforge.org/doc/nh/en/index.html的4.4. Dynamic models章節
這裡從簡設定
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                    assembly="NHibernateDemo"
                    namespace="NHibernateDemo.Domain">
  <!--assembly和namespace記得填專案名稱-->
  <class name="Persons">
    <!--主鍵-->
    <id name="PersonID"    >
      <generator class="identity"  />
    </id>
    <property name="PersonName"   />
  </class>
</hibernate-mapping>Step 6.加入一個類別MyHibernateHelper.cs 來初始化SessionFactory
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/*引用以下的命名空間*/
using NHibernate;
using NHibernate.Cfg;
using NHibernateDemo.Domain;
namespace NHibernateDemo
{
    //class前面追加public 
    public class MyHibernateHelper
    {
        private static ISessionFactory _sessionFactory;
        public static ISessionFactory SessionFactory
        {
            get
            {
                if (_sessionFactory == null)
                {
                    Configuration configuration = new Configuration();
                    configuration.Configure();//預設抓目錄下的hibernate.cfg.xml
                    //或者給絕對路徑↓
                    //configuration.Configure(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location)+@"\hibernate.cfg.xml");
                    configuration.AddAssembly(typeof(Persons).Assembly);
                    _sessionFactory = configuration.BuildSessionFactory();
                }
                return _sessionFactory;
            }
        }
        public static ISession OpenSession()
        {
            return SessionFactory.OpenSession();
        }
    }
}
Step 7.前置作業告一段落,可以開始存取資料庫了
新增一筆資料
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//引用以下命名空間
using NHibernate;
using NHibernateDemo.Domain;
namespace NHibernateDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            using (ISession session = MyHibernateHelper.SessionFactory.OpenSession())
            {
                using (ITransaction trans = session.BeginTransaction())
                {
                    Persons p = new Persons();
                    p.PersonName = "測試";
                    object obj = session.Save(p);//如果有設定show_sql為true的話,此句就會印出執行的SQL語句
                    Console.WriteLine("剛剛新增資料的主鍵:" + obj.ToString());
                    trans.Commit();
                }//交易失敗會自動Rollback
            }
            Console.ReadKey();//暫停畫面用
        }
    }
}
查詢資料
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//引用以下命名空間
using NHibernate;
using NHibernate.Criterion;
using NHibernateDemo.Domain;
namespace NHibernateDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            using (ISession session = MyHibernateHelper.SessionFactory.OpenSession())
            {
                int[] iCondition = { 1, 2 };//條件陣列
                //一般用法
                //var result = session.QueryOver<Persons>().Where(x=>x.PersonID==1).List();
                //var result = session.QueryOver<Persons>().Where(x => (x.PersonID >= 1 && x.PersonID <= 2)).List();
                //Like用法
                //var result = session.QueryOver<Persons>().Where(x => x.PersonName.IsLike("測試", MatchMode.Anywhere)).List();
                //in 的用法
                 var result = session.QueryOver<Persons>().Where(x => x.PersonID.IsIn(iCondition)).List();
                //not in 用法
                 //var result = session.QueryOver<Persons>().WhereNot(x => x.PersonID.IsIn(iCondition)).List();
                foreach (var item in result)
                {
                    Console.WriteLine("輸出一筆資料:"+item.PersonName);
                }
            }
            Console.ReadKey();//暫停畫面用
        }
    }
}
修改資料
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//引用以下命名空間
using NHibernate;
using NHibernate.Criterion;
using NHibernateDemo.Domain;
namespace NHibernateDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            using (ISession session = MyHibernateHelper.SessionFactory.OpenSession())
            {
                using (ITransaction trans = session.BeginTransaction())
                {
                    //找出DB的資料來Update
                    var result = session.QueryOver<Persons>().Where(x => x.PersonID.IsIn(new[] { 1, 2 })).List();
                    foreach (var item in result)
                    {
                        item.PersonName = "已修改";
                        session.Update(item);//這裡不會輸出SQL
                    }
                    //或建立一個想Update的物件(NHibernate會自行依主鍵找資料Update)
                    //Persons p = new Persons() { PersonID = 1, PersonName = "測試" };
                    //session.Update(p);
                    trans.Commit();//這裡才會真正執行Update
                }
            }//交易失敗會自動rollback
            Console.ReadKey();//暫停畫面用
        }
    }
}
刪除
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//引用以下命名空間
using NHibernate;
using NHibernate.Criterion;
using NHibernateDemo.Domain;
namespace NHibernateDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            using (ISession session = MyHibernateHelper.SessionFactory.OpenSession())
            {
                using (ITransaction trans = session.BeginTransaction())
                {
                    //找出DB的資料來Delete
                    var result = session.QueryOver<Persons>().Where(x => x.PersonID.IsIn(new[] { 1,2 })).List();
                    foreach (var item in result)
                    {
                        session.Delete(item);
                    }
                    //或建立一個想Delete的物件(NHibernate會自行依主鍵來Delete)
                    //Persons p = new Persons() { PersonID = 2 };
                    //session.Delete(p);
                    trans.Commit();//和上方的Delete()搭配才會輸出SQL
                }//交易失敗會自動Rollback
            }
            Console.ReadKey();//暫停畫面用
        }
    }
}
結語
本文只是讓初學者快速上手的懶人文章,基本操作大概如此
不過
如果您還沒使用過NHibernate,我會告訴您,這套很搞綱,導入專案會拖慢寫Code速度
微軟的Entity Framework已經很夠用,簡單好上手,請愛用自家人產品Entity Framework
真人Live Demo:
1. C# NHibernate 3.3 Setting up
2. C# NHibernate 3.3 Domain, Mapping and NHibernate Helper
3. C# NHibernate 3.3 CRUD Functionality
官網設定檔教學:
NHibernate Configuration - NHibernate blog - NHibernate Forge