11

コアがサードパーティへの直接参照を持たないコア部分とサードパーティモジュールを使用して、モジュール式の非モノリシックアプリケーションの設計でORM(この場合はEF5)を使用する方法についての提案を探していますモジュール、およびモジュールには、コア/共通のテーブルとクラスへの参照のみがあります。

議論のために、十分に近い類推は DNN です。

コードファースト:

CodeFirst で使用したアプローチは、リフレクションを介して Db のモデルを構築することでした。コアの DbContext の DbInitialation フェーズでは、Reflection を使用して、IDbInitializer (カスタムExecute() メソッドを含むコントラクト) を使用して、dll の構造のみを定義します。各 dll は、それ自体について知っていることを DbModel に追加しました。後続の Seeding も同じ wa で処理されました (特定の IDbSeeder コントラクトを検索して実行します)。

長所: * このアプローチは今のところ有効です。* 各レポが dbContext.GetSet() を使用する限り、それが dbContext のプロパティであると想定するのではなく、同じコア DbContext をすべてのレポジトリで使用できます。大したことはありません。短所: * 起動時にのみ機能します (つまり、新しいモジュールを追加するには、AppPool の更新が必要になります)。* CodeFirst は POC に最適です。しかし、EF5 では、エンタープライズ向けの作業にはまだ十分に成熟していません(そして、StoredProcs やその他の機能が追加される EF6 が待ちきれません)。* 私の DBA は CodeFirst を嫌います。少なくとも Core については、可能な限り Stored Procs でその部分を最適化したいと考えています...私たちはチームなので、できれば彼を喜ばせる方法を見つけなければなりません方法を見つける...

データベースファースト:

DbModel フェーズは、DbContext のコンストラクター (埋め込まれた *.edmx リソース ファイルからの読み取り) の前に発生しているようです。DbInitialization が呼び出されることはありません (モデルが完全であると見なされるため)。そのため、コアが認識している以上のテーブルを追加することはできません。

CodeFirst でできるように、動的にモデルに要素を追加できない場合は、コア DbContext のモデルが Db 内のすべてのテーブル (コアおよびすべてのサードパーティ モジュール) の知識を持っている必要があることを意味します。アプリケーションをモノリシックにして高度に結合し、まさに私が達成しようとしているものを無効にします。* または、各サード パーティが独自の DbContext を作成し、Core テーブルをインポートする必要があり、* バージョン管理の問題 (コアの *.edmx が更新されたときにモジュールが *.edmx を更新しないなど) * どこでも、異なるメモリ コンテキストでの重複 = ハード並行性の問題を追跡します。

現時点では、CodeFirst アプローチが、モジュラー ソフトウェアを EF で実現できる唯一の方法であるように思えます。しかし、他の誰かが DatabaseFirst を輝かせる方法を知っていることを願っています。埋め込まれた *.edmx ファイルから作成されたモデルに DbSet を「追加」する方法はありますか?

または他のアイデアはありますか?

4

2 に答える 2

4

pluginアプリケーション自体の全体的な設計であるため、一種のアーキテクチャを使用することを検討します。

次のようなことを行うことで、これの基本を達成できます (この例では使用していることに注意してください- here はStructureMap ドキュメントStructureMapへのリンクです)。

  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 classDbContextIPluginContext

    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オブジェクトには、独自のデータベース コンテキストを含めることができ、(必要に応じて) 独自のデータベース インスタンス/テーブルをインストールするプロセスを管理し、適切に破棄することができます。

明らかに、これは完全な解決策ではありませんが、役に立つ方向への出発点となることを願っています。:) ここで何かを明確にするのに役立つ場合は、お知らせください。

于 2012-11-08T00:24:18.123 に答える
1

これはCodeFirstのアプローチであり、最もクリーンなソリューションではありませんが、ここに投稿された回答のように、起動後でも初期化を強制的に実行するのはどうですか: Forcecing code-first to always initialize a non-existent database? (私は CodeFirst について正確には何も知りませんが、これが 1 か月前に投稿されていないことを考えると、試してみる価値があります)

于 2012-11-07T21:23:44.750 に答える