Fluent Nhibernate之旅(五)--利用AutoMapping進行簡單開發


Fluent Nhibernate(以下簡稱FN)發展到如今,已經相當成熟了,在Nhibernate的書中也相應的推薦了使用FN來進行映射配置,之前寫的FN之旅至今還有很多人會來私信我問題,說來慚愧,從FN之旅四至今已經4年多,至今還未更新過此系列,原因有很多,最大的就是懶惰,哈。

安裝

現在在項目中使用FN很方便,使用Nuget管理就可以了,但我還是建議大家,可以下載源代碼,自己可以詳細了解下。

image

當然,您也可以用命令台來進行安裝。說個題外話,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類型,接下來會說的。

數據庫結構

接下來,我們設計一個數據庫結構,簡單點:

image

員工,倉庫,產品,對應關系也全部到位了

實體類代碼

我們來編寫對應的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();
            }
        }

我們利用XUnit進行測試下,您會發現報錯:

image

這是為什么呢?這個其實就是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();
            }
        }

在進行下測試,這下我們通過了:

image

這里我只測試了Employee和Store兩個實體,對於多對多還未測試,時間有限,等有時間下篇繼續下。今天介紹了AutoMapping簡單的介紹,但在使用中您要時刻注意,AutoMapping是有很多限制的,比如Id,比如Component等,當然我們可以通過重寫DefaultAutomappingConfiguration的一些方法來進行合適的一些配置,如果您要對持久類有一些要求,不對一對多的LazyLoad或者Cascade之類的,您需要通過實現IReferenceConvention,IHasManyConvention,IHasManyToManyConvention進行配置,今天就不多講了,下次吧。

Fluent Nhibernate確實是個好東西,讓我在開發上省去了很多時間,今天雖然介紹了AutoMapping,但我不推薦您在您的項目中使用,用起來沒手感(個人感覺),需要約定的東西太多了,對於數據庫結構也得按照他的契約來,否則就得自己重寫一些方法,實現一些類,有點累人。但在開發一些小工具時,又要用到小型存儲數據的話,不妨可以試試這種方式。

PS:今天本來心情不錯,沒想到有客人來拉橫幅,一直在公司吵鬧,害的我無法集中精力,估計在文章中會有錯的地方,大家見諒了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM