11

MEF を使用する MVC 3 アプリケーションを構築しています。主なアイデアは、モデル、コントローラー、およびビューが実行時に mef コンテナーから動的に読み込まれるプラグイン メカニズムを持つことです。

各プラグイン/モジュールは 2 つのアセンブリで構成されています。

  • Module1.Data.dll (モデルの定義を含む)
  • Module1.Web.dll (コントローラーとビューを含む)

Web アプリケーションの bin 内の Plugins ディレクトリに配置されます。

  • WebApp/Bin/Plugins/Module1.Data.dll
  • WebApp/Bin/Plugins/Module1.Web.dll
  • WebApp/ビン/プラグイン/Module2.Data.dll
  • WebApp/ビン/プラグイン/Module2.Web.dll
  • WebApp/ビン/プラグイン/ModuleCore.Data.dll
  • WebApp/ビン/プラグイン/ModuleCore.Web.dll
  • 等...

他のすべてのモジュールによって参照されるコア モジュールもあります: ModuleCore.Data.dll およびそれぞれ ModuleCore.Web.dll。

次に、Global.asax で、次のようにコンテナーがビルドされます。

AggregateCatalog catalog = new AggregateCatalog();
var binCatalog = new DirectoryCatalog(HttpRuntime.BinDirectory, "Module*.dll");
var pluginsCatalot = new DirectoryCatalog(Path.Combine(HttpRuntime.BinDirectory, "Plugins"), "Module*.dll");
catalog.Catalogs.Add(binCatalog);
catalog.Catalogs.Add(pluginsCatalot);
CompositionContainer container = new CompositionContainer(catalog);
container.ComposeParts(this);
AppDomain.CurrentDomain.AppendPrivatePath(Path.Combine(HttpRuntime.BinDirectory, "Plugins"));

CustomViewEngine が作成および登録され、モジュール アセンブリでビューを検索するために使用されます。

ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new CustomViewEngine());

コンテナからコントローラをロードするためのコントローラ ファクトリ:

ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory(_container));

また、コンテナーからアセンブリを取得するためのカスタム仮想パス プロバイダー:

HostingEnvironment.RegisterVirtualPathProvider(new ModuleVirtualPathProvider());

これで、プラグ可能なモデル、コントローラー、およびビューを処理するためのインフラストラクチャ全体の準備が整いました。これですべてが機能します... 1 つのことを除いて -強く型付けされたビュー

問題をより詳細に説明するために、シーンを準備しましょう。

  • UserDTO モデルは Module1.Data.dll にあります
  • ShowUserController.cs は Module1.Web.dll/Controllers/ にあります。
  • Index.cshtml は Module1.Web.dll/Views/ShowUser にあります (@model Module1.Data.UserDto が宣言されています)

次に、次のことを行います。

  1. アプリケーションを実行し、HOST/ShowUser/Index に移動します (アクション メソッド Index が ShowUserController で実行され、ビュー Index.cshtml がフェッチされます)。
  2. ビュー Index.cshtml がフェッチされた後、コンパイルが開始されます (RazorBuildProvider による)。
  3. 例外がスローされます: 「名前空間 Module1 でデータ型が見つかりません」、つまり、ビューを動的に構築するときに UserDTO が見つかりませんでした

そのため、コンパイラ/ビルダーは Module1.Data.dll の bin/Plugins フォルダーを調べなかったようです。

質問/問題:このディレクトリが AppDomain.CurrentDomain.AppendPrivatePath メソッドによって追加されたにもかかわらず、ビルダーが bin/Plugins フォルダーを調べなかったのはなぜですか? プラグインフォルダーが考慮されるように、アセンブリビルダーのプライベートパスを一度追加する方法は??

標準のものをオーバーライドする CustomRazorBuildProvider を作成することで、いくつかの回避策を実行できました。

public class CustomRazorBuildProvider : RazorBuildProvider
{
  public override void GenerateCode(System.Web.Compilation.AssemblyBuilder assemblyBuilder)
  {
    Assembly a = Assembly.LoadFrom(Path.Combine(HttpRuntime.BinDirectory, "Plugins", "Module1.Data.dll"));
    assemblyBuilder.AddAssemblyReference(a);      
    base.GenerateCode(assemblyBuilder);
  }
} 

ただし、このソリューションの欠点は、ビューがコンパイルされるたびに Plugins フォルダー内のすべてのアセンブリへの参照を追加する必要があることです。これにより、後で多くのプラグインが使用されるときにパフォーマンスの問題が発生する可能性があります。

より良い解決策はありますか?

4

1 に答える 1

1

ここに考えがあります。

ビュー モデル パターンに従う場合は、DTO をビューに直接送信する代わりに、ビューと同じアセンブリに配置されるビューモデルを使用します。

代わりに:

UserDTO モデルは Module1.Data.dll にあります ShowUserController.cs は Module1.Web.dll/Controllers/ にあります Index.cshtml は Module1.Web.dll/Views/ShowUser にあります (宣言された @model Module1.Data.UserDto を使用)

次のようになります。

UserDTO モデルは Module1.Data.dll にあります。 ShowUserController.cs は Module1.Web.dll/Controllers/ にあります。 UserVM は Module1.Web.dll/ViewModels にあります Index.cshtml は Module1.Web.dll/Views/ShowUser にあります (宣言された @model Module1.Web.ViewModels.UserVM を使用)

Controller に DTO を ViewModel にマップさせる

マッピングに役立つAutoMapperを参照してください

于 2012-11-21T14:43:18.270 に答える