2012-09-29 47 views
11

我正在尋找關於如何在模塊化非單片應用程序的設計中使用ORM(在這種情況下爲EF5)的建議,核心部分和第三方模塊,其中核心沒有直接引用第三方模塊,模塊僅引用核心/公用表和類。EF:爲模塊化應用程序的DatabaseFirst DbContext尋找設計策略

爲了討論起見,一個足夠接近的比喻應該是DNN。

CodeFirst:

隨着CodeFirst,我採用的方法是建立數據庫的模型是通過反射:在覈心的的DbContext的DbInitialation階段,我用思考來找到任何DLL任何類(如,芯或各種模塊)用IDbInitializer(包含Execute()方法的自定義合同)裝飾來定義dll的結構。每個dll添加到DbModel中它對自身的瞭解。 任何後續的Seeding也在同一個wa中處理(搜索特定的IDbSeeder合同,並執行它)。 *該方法目前適用。 *只要每個repo使用dbContext.GetSet(),而不是期望它是dbContext的屬性,則可以在所有存儲庫中使用相同的核心DbContext。沒什麼大不了的。 缺點: *它只在啓動時起作用(即添加新模塊需要刷新AppPool)。 * CodeFirst非常適合POC。但在EF5中,它還不夠成熟,因爲企業工作還有(我不能等待EF6用於StoredProcs和其他功能要添加)。 *我的DBA討厭CodeFirst,至少對於Core來說,希望儘可能多地使用Stored Procs來優化這個部分......我們是一個團隊,所以我必須設法找到一種讓他高興的方式,如果我能找到一種方法...

數據庫一:

的DbModel之後階段似乎到的DbContext的構造函數之前發生的事情(從嵌入的.edmx *資源文件中讀取)。永遠不會調用DbInitialization(因爲模型被認爲是完整的),所以我不能添加比Core所知更多的表。

如果我無法像使用CodeFirst一樣動態地將元素添加到模型中,則表示 *要麼Core DbContext的模型必須知道Db-Core中的每個表以及每個第3方模塊。使應用程序單片和高度耦合,擊敗我試圖實現的東西。 *或每個第三方必須創建自己的DbContext,導入Core表,導致 *版本問題(當Core的* .edmx更新時,模塊不更新* .edmx等) *重複隨處可見,在不同的內存中上下文=很難追蹤併發問題。

在這一點上,在我看來,CodeFirst方法是使用EF實現模塊化軟件的唯一方法。但希望別人知道如何讓DatabaseFirst閃耀 - 是否有任何方式將'DbSet'附加到從嵌入式* .edmx文件創建的模型中?

還是其他想法?

回答

4

我會考慮使用排序plugin架構的,因爲這是你的應用程序自身的整體設計。

您可以通過執行類似下面的完成這一基本(注意,這個例子使用StructureMap - 這裏是一個鏈接到StructureMap Documentation):

  1. 創建的界面,您DbContext對象可以派生。

    public interface IPluginContext { 
        IDictionary<String, DbSet> DataSets { get; } 
    } 
    
  2. 在你的依賴注入建立(使用StructureMap) - 這樣做如下:

    Scan(s => { 
        s.AssembliesFromApplicationBaseDirectory(); 
        s.AddAllTypesOf<IPluginContext>(); 
        s.WithDefaultConventions(); 
    }); 
    For<IEnumerable<IPluginContext>>().Use(x => 
        x.GetAllInstances<IPluginContext>() 
    ); 
    
  3. 對於每一個插件,或者改變{plugin}.Context.tt文件 - 或添加partial class導致生成DbContext的文件從IPluginContext派生。

    public partial class FooContext : IPluginContext { } 
    
  4. 改變{plugin}.Context.tt文件爲每個插件揭露類似:

    public IDictionary<String, DbSet> DataSets { 
        get { 
         // Here is where you would have the .tt file output a reference 
         // to each property, keyed on its property name as the Key - 
         // in the form of an IDictionary. 
        } 
    } 
    
  5. 現在,您可以像下面這樣:

    // This could be inside a service class, your main Data Context, or wherever 
    // else it becomes convenient to call. 
    public DbSet DataSet(String name) { 
        var plugins = ObjectFactory.GetInstance<IEnumerable<IPluginContext>>(); 
        var dataSet = plugins.FirstOrDefault(p => 
         p.DataSets.Any(ds => ds.Key.Equals(name)) 
        ); 
    
        return dataSet; 
    } 
    

原諒我,如果語法並不完美 - 我在帖子中這樣做,而不是在編譯器中。

最終的結果讓您可以靈活地做一些事情,如:

// Inside an MVC controller... 
    public JsonResult GetPluginByTypeName(String typeName) { 
     var dataSet = container.DataSet(typeName); 
     if (dataSet != null) { 
      return Json(dataSet.Select()); 
     } else { 
      return Json("Unable to locate that object type."); 
     } 
    } 

顯然,在長期的 - 你想控制將被倒置,這裏的插件實際上是一個綁定到架構中,而不是服務器期待的類型。您可以使用完成這種延遲加載的這樣的事情,但是 - 其中主要的應用暴露了一個終點,所有的插件領帶來。

這將是這樣的:

public interface IPlugin : IDisposable { 
    void EnsureDatabase(); 
    void Initialize(); 
} 

您現在可以公開此接口的任何應用程序開發誰想要爲你的架構(DNN風格)創建的插件 - 和你的StructureMap配置工作是這樣的:

Scan(s => { 
    s.AssembliesFromApplicationBaseDirectory(); // Upload all plugin DLLs here 
    // NOTE: Remember that this gives people access to your system!!! 
    //  Given what you are developing, though, I am assuming you 
    //  already get that. 
    s.AddAllTypesOf<IPlugin>(); 
    s.WithDefaultConventions(); 
}); 
For<IEnumerable<IPlugin>>().Use(x => x.GetAllInstances<IPlugin>()); 

現在,當你初始化應用程序,你可以這樣做:

// Global.asax 
public static IEnumerable<IPlugin> plugins = 
    ObjectFactory.GetInstance<IEnumerable<IPlugin>>(); 

public void Application_Start() { 
    foreach(IPlugin plugin in plugins) { 
     plugin.EnsureDatabase(); 
     plugin.Initialize(); 
    } 
} 

您的每個IPlugin對象現在都可以包含其自己的數據庫上下文,管理安裝(如果需要)其自己的數據庫實例/表的過程,並優雅地處置其自身。

很明顯,這不是一個完整的解決方案 - 但我希望它能讓你在有用的方向開始。 :)如果我可以幫助澄清此處的任何內容,請讓我知道。