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 が宣言されています)
次に、次のことを行います。
- アプリケーションを実行し、HOST/ShowUser/Index に移動します (アクション メソッド Index が ShowUserController で実行され、ビュー Index.cshtml がフェッチされます)。
- ビュー Index.cshtml がフェッチされた後、コンパイルが開始されます (RazorBuildProvider による)。
- 例外がスローされます: 「名前空間 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 フォルダー内のすべてのアセンブリへの参照を追加する必要があることです。これにより、後で多くのプラグインが使用されるときにパフォーマンスの問題が発生する可能性があります。
より良い解決策はありますか?