Fluent Nhibernate(以下簡稱FN)發展到如今,已經相當成熟了,在Nhibernate的書中也相應的推薦了使用FN來進行映射配置,之前寫的FN之旅至今還有很多人會來私信我問題,說來慚愧,從FN之旅四至今已經4年多,至今還未更新過此系列,原因有很多,最大的就是懶惰,哈。
安裝
現在在項目中使用FN很方便,使用Nuget管理就可以了,但我還是建議大家,可以下載源代碼,自己可以詳細了解下。
當然,您也可以用命令台來進行安裝。說個題外話,NuGet真心不錯,至少已經做新項目的時候不用到處去整理lib包了,從NuGet進行下載、更新、卸載都很方便,而且還能自搭建自己公司的服務器,不過目前還是有些問題,有時候在下載最新包無法使用的情況下,無法自動降低版本,還得自己去手動控制台下載,非常不便。
在NuGet下載FluentNhibernate后,會自動下載安裝Nhibernate及Iesi。
配置
准備工作完成,今天我們要說下FN的AutoMapping,之前呢我們都是用了FluentMapping進行手動的Map映射,如果可以的話,請大家還是使用之前的映射方式,因為AutoMapping有很多契約,您需要按照一定的規范編寫您的對象。
我們先看下ISessionFactory中的Mapping配置:
private static ISessionFactory CreateSessionFactory() { return Fluently.Configure() .Database(MsSqlConfiguration.MsSql2005 .ConnectionString(s => s.Server(".") .Database("MyNHibernate") .TrustedConnection())) .Mappings(m => m.FluentMappings.Add<Store>()) .BuildSessionFactory(); }
這是我們手動映射,Mapping()中指定了用FluentMappings進行映射,我們只要把它改成AutoMapping:
private static ISessionFactory CreateSessionFactory() { return Fluently.Configure() .Database(MsSqlConfiguration.MsSql2005 .ConnectionString(s => s.Server(".") .Database("MyNHibernate") .TrustedConnection())) .Mappings(m => m.AutoMappings.Add(AutoMap.AssemblyOf<Store>())) .BuildSessionFactory(); }
紅色字體為不同之處,Store是我們需要映射的實體類,可能您會用為何不能像FluentMapping一樣Add<T>()呢?因為這里考慮到一些契約的問題,它的Add參數為AutoPersistenceModel類型,接下來會說的。
數據庫結構
接下來,我們設計一個數據庫結構,簡單點:
員工,倉庫,產品,對應關系也全部到位了
實體類代碼
我們來編寫對應的Model代碼:
public class Store { public Store() { Products = new List<Product>(); } public virtual int Id { get; set; } public virtual string Name { get; set; } public virtual IEnumerable<Product> Products { get; set; } } public class UserName { public string FirstName { get; set; } public virtual string LastName { get; set; } } public class Employee { public virtual int Id { get; set; } public virtual UserName Name { get; set; } public virtual Store Store { get; set; } } public class Product { public Product() { Stores = new List<Store>(); } public virtual int Id { get; set; } public virtual float Price { get; set; } public virtual string Name { get; set; } public virtual IEnumerable<Store> Stores { get; set; } }
好了,代碼編寫完畢,接下來是映射嗎???No,無需映射了,因為我們用了AutoMapping,一切交給FN吧,我們接下來只要編寫測試代碼即可。
測試
我們先配置ISessionFactory:
private static string dbfile = ConfigurationManager.AppSettings["dbfile"]; private static ISessionFactory CreateSessionFactory() { return Fluently.Configure() .Database(SQLiteConfiguration.Standard.UsingFile(dbfile)) .Mappings(m => m.AutoMappings.Add(AutoMap.AssemblyOf<Store>())) .ExposeConfiguration(BuildSchema) //利用Nhibernate的SchemaExport創建數據庫及其架構 .BuildSessionFactory(); } private static void BuildSchema(NHibernate.Cfg.Configuration obj) { //delete the existing db on each run if (File.Exists(dbfile)) File.Delete(dbfile); new SchemaExport(obj).Create(false, true); }
在這里,我用了Sqlite做為我們的數據庫,ORM最大的好處就是我們可以隨意的變更我們的數據庫類型,不需要考慮其類型,這是我選擇Nhibernate的原因,EF雖然通過擴展能夠支持其他數據庫,但我相信用EF使用其他數據庫的人很少吧。用ExposeConfiguration方法委托Nhibernate的SchemaExport來創建數據庫架構,相信很多NH玩家都會用吧。不過不建議把它用在稍大的項目里,我們的項目數據庫會隨着項目需求的增加和改變會經常修改的,建議用專業的數據庫管理,比如Migrator,有興趣下次可以開篇介紹下,用下來還是不錯的。
[Fact] public void DemoTest() { using (var session = CreateSessionFactory().OpenSession()) { new PersistenceSpecification<Employee>(session) .CheckProperty(c => c.Id, 1) .CheckProperty(c => c.Name.FirstName, "James") .CheckProperty(c => c.Name.LastName, "YinG") .CheckReference(c => c.Store, new Store() { Name = "MyStore" }) .VerifyTheMappings(); } }
這是為什么呢?這個其實就是AutoMapping在處理映射的時候,都是按照一定的規則去映射的,細心的朋友可能發現,我們的索引字段的字段名都是Id,這就是AutoMapping的約定,我們也可以自己來進行配置。
剛我們的測試報錯,是因為Employee中的Name是個UserName類,Nhibernate的Component,我在FN之旅四(上)中有介紹到,默認情況下映射根據字段映射到數據庫的,所以上面的測試會報錯,接下來我們自己配置下:
編寫一個類,繼承DefaultAutomappingConfiguration:
public class CustomConfiguration : DefaultAutomappingConfiguration { public override bool IsComponent(Type type) { return type == typeof(UserName); } }
重寫IsComponent方法,簡單吧,核對下類型即可,寫完自己的規則后,我們需要在建立SessionFactory的時候引入此配置:
.Mappings(m => m.AutoMappings.Add(AutoMap.AssemblyOf<Store>(new CustomConfiguration())))
現在您還無法測試通過,因為我用了PersistenceSpecification進行測試的,在進行常規的測試時是沒有問題的,但遇到Component或者Reference之類的,都需要我們自己來寫一個IEqualityComparer的實現:
public class UserNameComparer : IEqualityComparer { public new bool Equals(object x, object y) { var username_x = x as UserName; if (username_x == null) return false; var username_y = y as UserName; if (username_y == null) return false; return username_x.FirstName.Equals(username_y.FirstName) && username_x.LastName.Equals(username_y.LastName); } public int GetHashCode(object obj) { throw new NotImplementedException(); } } public class StoreComparer : IEqualityComparer { public new bool Equals(object x, object y) { var Store_x = x as Store; if (Store_x == null) return false; var Store_y = y as Store; if (Store_y == null) return false; return Store_x.Id == Store_y.Id; } public int GetHashCode(object obj) { throw new NotImplementedException(); } }
好,修改下我們的測試代碼:
[Fact] public void DemoTest() { using (var session = CreateSessionFactory().OpenSession()) { new PersistenceSpecification<Employee>(session) .CheckProperty(c => c.Id, 1) .CheckProperty(c => c.UserName, new UserName{ FirstName = "James", LastName = "YinG" }, new UserNameComparer()) .CheckReference(c => c.Store, new Store() { Name = "MyStore" }, new StoreComparer()) .VerifyTheMappings(); } }
這里我只測試了Employee和Store兩個實體,對於多對多還未測試,時間有限,等有時間下篇繼續下。今天介紹了AutoMapping簡單的介紹,但在使用中您要時刻注意,AutoMapping是有很多限制的,比如Id,比如Component等,當然我們可以通過重寫DefaultAutomappingConfiguration的一些方法來進行合適的一些配置,如果您要對持久類有一些要求,不對一對多的LazyLoad或者Cascade之類的,您需要通過實現IReferenceConvention,IHasManyConvention,IHasManyToManyConvention進行配置,今天就不多講了,下次吧。
Fluent Nhibernate確實是個好東西,讓我在開發上省去了很多時間,今天雖然介紹了AutoMapping,但我不推薦您在您的項目中使用,用起來沒手感(個人感覺),需要約定的東西太多了,對於數據庫結構也得按照他的契約來,否則就得自己重寫一些方法,實現一些類,有點累人。但在開發一些小工具時,又要用到小型存儲數據的話,不妨可以試試這種方式。
PS:今天本來心情不錯,沒想到有客人來拉橫幅,一直在公司吵鬧,害的我無法集中精力,估計在文章中會有錯的地方,大家見諒了。




