深度剖析Byteart Retail案例:基於MongoDB的倉儲實現


今天花了半天時間,向Byteart Retail案例加入了基於MongoDB的倉儲實現,讀者朋友可以直接從Byteart Retail的代碼庫克隆最新代碼來使用基於MongoDB的倉儲實現。

實現步驟

1、重構ByteartRetail.Domain.Repositories目錄結構

本來這一步是不需要做的,但是因為之前沒有把結構規划好,所以所有基於Entity Framework的倉儲實現都放在了根目錄下。現在把這些倉儲的實現都移到了EntityFramework目錄中,同時修改了命名空間和ByteartRetail.Services項目的web.config文件。改完后結構如下:

image

2、實現基於MongoDB的倉儲和上下文

在《深度剖析Byteart Retail案例:倉儲(Repository)及其上下文(Repository Context)》一文中,我已經詳細介紹了Byteart Retail案例中倉儲及其上下文的設計和實現,因此,在已有的框架上再實現一個MongoDB的倉儲是非常容易的事情,具體實現方式在此也不多做說明了,可以結合這篇文章並參考源代碼,整個過程只花了我不到一個小時的時間。實現后的目錄結構如下:

image

3、將SQL LocalDB中的數據遷移到MongoDB中

這部分花了我一些時間,為了簡單起見,我還是自己寫了一些控制台代碼,基本思路是:先用EntityFrameworkRepository將對象讀入,然后以聚合根為單位,使用MongoDBRepository依次寫入MongoDB。寫入的時候遇到了一些小問題,其中最需要注意的就是,在我們的SalesLine對象中聚合了SalesOrder,而SalesOrder本身又聚合了SalesLine,這就造成了循環引用,因此MongoDB會報錯的(EF不會報錯,因為EF采用了延遲加載功能來獲取SalesLine信息),為了解決這個問題,需要配置MongoDB的Class Map,如下:

BsonClassMap.RegisterClassMap<SalesLine>(s =>
{
    s.AutoMap();
    s.SetIgnoreExtraElements(true);
    s.UnmapProperty<SalesOrder>(p => p.SalesOrder); // bypass circular reference.
});

此外,我們需要用聚合根的ID作為MongoDB的objectID,並希望日期時間以本地時間格式存儲,因此,我另外開發了兩個Convention Profile,並在MongoDBRepositoryContext中增加了兩個靜態方法,以便應用程序在啟動的時候能夠調用這個靜態方法完成相關設置:

public class UseLocalDateTimeConvention : IMemberMapConvention
{
    public void Apply(BsonMemberMap memberMap)
    {
        IBsonSerializationOptions options = null;
        switch (memberMap.MemberInfo.MemberType)
        {
            case MemberTypes.Property:
                PropertyInfo propertyInfo = (PropertyInfo)memberMap.MemberInfo;
                if (propertyInfo.PropertyType == typeof(DateTime) ||
                    propertyInfo.PropertyType == typeof(DateTime?))
                    options = new DateTimeSerializationOptions(DateTimeKind.Local);
                break;
            case MemberTypes.Field:
                FieldInfo fieldInfo = (FieldInfo)memberMap.MemberInfo;
                if (fieldInfo.FieldType == typeof(DateTime) ||
                    fieldInfo.FieldType == typeof(DateTime?))
                    options = new DateTimeSerializationOptions(DateTimeKind.Local);
                break;
            default:
                break;
        }
        memberMap.SetSerializationOptions(options);
    }
    public string Name
    {
        get { return this.GetType().Name; }
    }
}

public class GuidIDGeneratorConvention : IPostProcessingConvention
{
    public void PostProcess(BsonClassMap classMap)
    {
        if (typeof(IEntity).IsAssignableFrom(classMap.ClassType) && 
            classMap.IdMemberMap != null)
        {
            classMap.IdMemberMap.SetIdGenerator(new GuidGenerator());
        }
    }
    public string Name
    {
        get { return this.GetType().Name; }
    }
}

public static void RegisterConventions(bool autoGenerateID = true, bool localDateTime = true)
{
    RegisterConventions(autoGenerateID, localDateTime, null);
}

public static void RegisterConventions(bool autoGenerateID, bool localDateTime, 
    IEnumerable<IConvention> additionConventions)
{
    var conventionPack = new ConventionPack();
    conventionPack.Add(new NamedIdMemberConvention("id", "Id", "ID", "iD"));
    if (autoGenerateID)
        conventionPack.Add(new GuidIDGeneratorConvention());
    if (localDateTime)
        conventionPack.Add(new UseLocalDateTimeConvention());
    if (additionConventions != null)
        conventionPack.AddRange(additionConventions);

    ConventionRegistry.Register("DefaultConvention", conventionPack, t => true);
}

4、修改Global.asax.cs文件

在Global.asax.cs文件中,找到Application_Start方法,在這個方法中加入bootstrapper的調用:MongoDBBootstrapper.Bootstrap();。

當然,為了簡單起見,我還引入了一個MongoDBBootstrapper類,用來調用上面的RegisterConventions方法對Convention Profile進行注冊,同時還包含了對Class Map的注冊,在此就不貼代碼了。完成以上四步以后,准備工作就做好了。

啟用MongoDB倉儲及其上下文

准備工作做好以后,就讓我們開始啟用MongoDB倉儲吧。由於我上面已經完成了數據遷移,因此,在我的MongoDB數據庫中已經有了ByteartRetail數據庫。讀者朋友在完成最新版本代碼克隆之后,請先自行安裝MongoDB服務,然后,在Byteart Retail源代碼目錄的demo_data目錄下,有個ByteartRetail.zip的壓縮包,將其解壓到你的本地磁盤,然后,進入MongoDB的bin目錄,使用mongorestore.exe程序恢復ByteartRetail數據庫。比如:

mongorestore -d ByteartRetail c:\ByteartRetail

完成數據庫恢復之后,你將可以在mongoDB的提示符下列出ByteartRetail數據庫:

image

下一步,修改ByteartRetail.Services下的web.config文件,將unity配置部分中的Entity Framework倉儲及其上下文的配置部分注釋掉,然后啟用MongoDB倉儲及其上下文的配置,如下:

image

OK,現在啟動站點,從前台的角度我們基本上看不到任何變化,但此時Byteart Retail已經在使用MongoDB作為數據持久化機制了。

image

最后說明一下:如果你安裝MongoDB的時候不是使用的默認配置(比如你改過MongoDB的端口等設置),那么,你可以修改ByteartRetail.Domain.Repositories.MongoDB.MongoDBRepositoryContextSettings 類,在這個類中根據你的具體情況對MongoDB進行配置。配置的詳細說明請參考該類中的注釋。

總結

本文也算是《深度剖析Byteart Retail案例》系列文章的一個題外篇,主要目的就是為了驗證在Byteart Retail案例中,倉儲及其上下文的可擴展性。實驗證明,目前的框架設計能夠在不改動任何已有組件的基礎上,直接新增對其它數據持久化方案的支持,我們所要做的僅僅就是繼承幾個類、實現幾個接口,然后修改一下配置信息。整個過程花了我一個人不到半天的時間,當然,Byteart Retail項目本身規模也不大,但仍然給了我們很好的啟示:良好的架構設計能夠大幅度降低團隊資源的浪費,並且減小出錯的幾率,同時還對系統運營提供了一定的支持:我們可以讓某個團隊單獨地開發一個組件,在完成開發和測試之后,可以在不大范圍影響系統運行的基礎上將新的實現替換進去。因此,良好的架構設計將會對項目的管理帶來重大影響。


免責聲明!

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



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