NHibernate 3.2 Mapping By Code 實作範例

NHibernate 3.2 Mapping By Code 實作範例

繼Linq to SQL、Entity Framework之後,最近又碰了一套ORM就是"NHibernate"

在初步的認識他之後,覺得這套的擴充性與功能方面都十分強大。

當然目前對他的掌握度還沒有很高,因此可能寫不出內容豐富的文章

但這篇要介紹的是 NHibernate 3.2 內建的Mapping By Code,將過去資料庫與類別

對應的xml改為到cs檔裡面撰寫,除了有強型別好處外,也可讓開發人員

更容易設計自己的類別。

 

本篇利用ASP.NET MVC + NHibernate 3.2 實作

資料庫選用SQLite,並利用Code First在執行時期建立出資料庫

由於網路上目前NHibernate與Mapping By Code的中文文章相對的不多,因此會

著重NHibernate這個部分,並簡單的做出一個CRUD的功能。

 

首先建立一個新的ASP.NET MVC 2空白專案,並利用NeGet 抓取NHibernate 3.2 版

image

搜尋NHibernate,並且安裝

image

 

安裝好後,開始寫第一個Product類別

Product.cs

    public class Product
    {
        public virtual int Id { get; set; }
        public virtual string Name { get; set; }
        public virtual int Price { get; set; }
        public virtual int Quantity { get; set; }
        public virtual int CreateUserId { get; set; }
        public virtual DateTime CreateTime { get; set; }
        public virtual int? EditUserId { get; set; }
        public virtual DateTime? EditTime { get; set; }
    }

注意!!所有成員都必須是virtual。

這個實體類別就是我們在程式中所使用的Product類別,我們可以依實際的需要設計這個類別

接著寫Product類跟資料庫要如何對應的ProductMapping類別

ProductMapping.cs

using System;
using NHibernate.Mapping.ByCode.Conformist;
using NHibernate.Mapping.ByCode;

namespace NHibernateMappingByCode.Models.Mapping
{
    //繼承ClassMapping<T>
    public class ProductMapping : ClassMapping<Product>
    {
        public ProductMapping()
        {
            /* 如果是共用的屬性可以自訂一個父類別去Mapping
             * 就可以不用每個類別都寫重複的對應        
             */
            //主鍵,產生的類型選擇Identity
            Id(p => p.Id, map => map.Generator(Generators.Identity));
            Property(p => p.CreateTime, map => map.NotNullable(true));
            Property(p => p.CreateUserId, map => map.NotNullable(true));
            Property(p => p.EditTime);
            Property(p => p.EditUserId);

            Property(p => p.Name, map => { map.Length(50); map.NotNullable(true); });
            Property(p => p.Price, map => map.NotNullable(true));
            Property(p => p.Quantity, map => map.NotNullable(true));
        }
    }
}

這邊採用的是Conformist Mapping,另外還有一種方式是下面這種Specific Mapper,就不介紹了。

var mapper = new ModelMapper();
mapper.Class<TestClass>(cm =>
{
    cm.Id(p => p.Id, map => map.Generator(Generators.Identity));
    cm.Property(p => p.Something);
});

關於Mapping的設定還有非常多,像一對多、多對一、多對多、Cache等..設定

等研究透徹一點後再慢慢分享出來。

寫完了我們的實體類別跟對應之後,接著就來設定連線

NHibernateUtility.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using NHibernate.Cfg;
using NHibernate;
using NHibernate.Dialect;
using System.Data;
using NHibernate.Cfg.MappingSchema;
using NHibernate.Mapping.ByCode;
using System.Reflection;
using NHibernateMappingByCode.Models.Mapping;

namespace NHibernateMappingByCode.Models
{
    public class NHibernateUtility
    {
        public static Configuration Configuration { get; private set; }

        public static ISessionFactory SessionFactory { get; private set; }

        public void Initialize()
        {
            Configuration = Configure();

            SessionFactory = Configuration.BuildSessionFactory();
        }

        private Configuration Configure()
        {
            var configuration = new Configuration();

            // 資料庫設定
            // 這裡的東西可以改用xml的方式設定,增加修改的彈性
            configuration.DataBaseIntegration(c =>
            {
                // 資料庫選用 SQLite
                c.Dialect<SQLiteDialect>();
                // 取用 .config 中的 "MyTestDB" 連線字串
                c.ConnectionStringName = "MyTestDB";
                // Schema 變更時的處置方式
                c.SchemaAction = SchemaAutoAction.Update;
                // 交易隔離等級
                c.IsolationLevel = IsolationLevel.ReadCommitted;
            });

            // 取得Mapping
            // 取代舊有的 *.hbm.xml
            var mapping = GetMappings();

            //加入Mapping
            configuration.AddMapping(mapping);

            return configuration;
        }

        private HbmMapping GetMappings()
        {
            var mapper = new ModelMapper();

            //加入Mapping
            //如果是多個 Type 可用 AddMappings
            mapper.AddMapping(typeof(ProductMapping));

            HbmMapping mapping = mapper.CompileMappingForAllExplicitlyAddedEntities();
            return mapping;
        }
    }
}

Configuration就是以往NHibernate設置在hibernate.cfg.xml的連線設定

當然設定在xml中修改後不需要重新編譯,使用上較方便,但這邊介紹的是另外一種寫法。

 

由於是Code First,因此在開發時完全不需要考慮到資料庫,這是讓我覺得Code First最

棒的一點,如果需要更換資料庫的話,只需要更改上方的

//c.Dialect<SQLiteDialect>();
//更換成使用 SQL 2008
c.Dialect<MsSql2008Dialect>();

並將連線字串改為SQL 2008資料庫的設定,在Application重啟時,就會在對應的資料庫

中生成Table。

 

接著在Global.asax.cs中,Application_Start()時加入

protected void Application_Start()
{
    NHibernateUtility utility = new NHibernateUtility();
    utility.Initialize();

    AreaRegistration.RegisterAllAreas();

    RegisterRoutes(RouteTable.Routes);
}

然後運行時就會把Table建出來了。

另外還有一點要注意的是,這邊SchemaActoin設定是Update

也就是Product類別與ProductMapping對應有改變時,就會Update資料庫的Schema

當系統上線後,不妨把這一行拿掉,以免不小心被誰改到了結果資料庫整個亂掉

 

到這邊為止我們已經有了一張Product Table

為了讓範例完整一點,就寫一個ProductService做出CRUD的功能

(這邊沒有特別去管理Session與實作Repository,因此僅供參考就好)

ProductService.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using NHibernate;
using NHibernate.Linq;

namespace NHibernateMappingByCode.Models
{
    public class ProductService
    {
        public IEnumerable<Product> GetAll()
        {
            IEnumerable<Product> model;
            using (var session = NHibernateUtility.SessionFactory.OpenSession())
            {
                model = session.Query<Product>().ToList();
            }
            return model;
        }

        public Product GetSingle(int id)
        {
            Product model;
            using (var session = NHibernateUtility.SessionFactory.OpenSession())
            {
                model = session.Get<Product>(id);
            }
            return model;
        }

        public void Insert(Product model)
        {
            model.CreateTime = DateTime.Now;
            model.CreateUserId = 1;

            //如同時處理好幾張表,可加入交易避免例外發生時產生髒資料
            using (var session = NHibernateUtility.SessionFactory.OpenSession())
            using (var trans = session.BeginTransaction())
            {
                try
                {
                    session.Save(model);
                    trans.Commit();
                }
                catch (Exception ex)
                {
                    // Log...
                    trans.Rollback();
                }
            }
        }

        public void Update(int id, Product model)
        {
             var soucre = GetSingle(id);

             if (soucre != null)
             {
                 soucre.Name = model.Name;
                 soucre.Price = model.Price;
                 soucre.Quantity = model.Quantity;
                 soucre.EditTime = DateTime.Now;
                 soucre.EditUserId = 1;

                 //如同時處理好幾張表,可加入交易避免例外發生時產生髒資料
                 using (var session = NHibernateUtility.SessionFactory.OpenSession())
                 using (var trans = session.BeginTransaction())
                 {
                     try
                     {
                         session.SaveOrUpdate(soucre);
                         trans.Commit();
                     }
                     catch (Exception ex)
                     {
                         // Log...
                         trans.Rollback();
                     }
                 }
             }
        }

        public void Delete(int id)
        {
            var model = GetSingle(id);

            if (model != null)
            {
                using (var session = NHibernateUtility.SessionFactory.OpenSession())
                using (var trans = session.BeginTransaction())
                {
                    try
                    {
                        session.Delete(model);
                        trans.Commit();
                    }
                    catch (Exception ex)
                    {
                        // Log...
                        trans.Rollback();
                    }
                }
            }
        }
    }
}

HomeController.cs

namespace NHibernateMappingByCode.Controllers
{
    public class HomeController : Controller
    {
        ProductService _prodcutService;

        public HomeController()
        {
            _prodcutService = new ProductService();
        }

        public ActionResult Index()
        {
            var model = _prodcutService.GetAll();
            return View(model);
        }

        public ActionResult Insert()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Insert(Product model)
        {
            _prodcutService.Insert(model);
            return RedirectToAction("Index");
        }

        public ActionResult Update(int id)
        {
            var model = _prodcutService.GetSingle(id);
            return View(model);
        }

        [HttpPost]
        public ActionResult Update(int id, Product model)
        {
            _prodcutService.Update(id, model);
            return RedirectToAction("Index");
        }

        public ActionResult Delete(int id)
        {
            _prodcutService.Delete(id);
            return RedirectToAction("Index");
        }
    }
}

 

 

範例畫面

image

image

隨文附上範例下載

SQLite的dll是用x86版本的,因此如果是x64的機器測試時要改一下