私は、あなたが説明したものと同様のプラグ可能なアーキテクチャを持ち、同じテクノロジASP.NETMVCとMEFを使用するプロジェクトに取り組んできました。認証、承認、およびすべての要求を処理するホストASP.NETMVCアプリケーションがありました。プラグイン(モジュール)はそのサブフォルダーにコピーされました。プラグインは、独自のモデル、コントローラー、ビュー、css、およびjsファイルを持つASP.NETMVCアプリケーションでもありました。これを機能させるために実行した手順は次のとおりです。
MEFの設定
MEFに基づいて、アプリケーションの開始時にすべての構成可能なパーツを検出し、構成可能なパーツのカタログを作成するエンジンを作成しました。これは、アプリケーションの開始時に1回だけ実行されるタスクです。エンジンは、すべてのプラグ可能なパーツを検出する必要があります。この場合bin
、ホストアプリケーションのModules(Plugins)
フォルダーまたはフォルダーのいずれかにあります。
public class Bootstrapper
{
private static CompositionContainer CompositionContainer;
private static bool IsLoaded = false;
public static void Compose(List<string> pluginFolders)
{
if (IsLoaded) return;
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new DirectoryCatalog(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin")));
foreach (var plugin in pluginFolders)
{
var directoryCatalog = new DirectoryCatalog(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Modules", plugin));
catalog.Catalogs.Add(directoryCatalog);
}
CompositionContainer = new CompositionContainer(catalog);
CompositionContainer.ComposeParts();
IsLoaded = true;
}
public static T GetInstance<T>(string contractName = null)
{
var type = default(T);
if (CompositionContainer == null) return type;
if (!string.IsNullOrWhiteSpace(contractName))
type = CompositionContainer.GetExportedValue<T>(contractName);
else
type = CompositionContainer.GetExportedValue<T>();
return type;
}
}
これは、すべてのMEFパーツの検出を実行するクラスのサンプルコードです。Compose
クラスのメソッドが呼び出されたからApplication_Start
でメソッドGlobal.asax.cs
ファイル。簡単にするために、コードは縮小されています。
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
var pluginFolders = new List<string>();
var plugins = Directory.GetDirectories(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Modules")).ToList();
plugins.ForEach(s =>
{
var di = new DirectoryInfo(s);
pluginFolders.Add(di.Name);
});
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
Bootstrapper.Compose(pluginFolders);
ControllerBuilder.Current.SetControllerFactory(new CustomControllerFactory());
ViewEngines.Engines.Add(new CustomViewEngine(pluginFolders));
}
}
すべてのプラグインはModules
、ホストアプリケーションのルートにあるフォルダーの別のサブフォルダーにコピーされると想定されています。各プラグインサブフォルダーには、Views
サブフォルダーと各プラグインのDLLが含まれています。Application_Start
上記の方法では、カスタムコントローラーファクトリと以下で定義するカスタムビューエンジンも初期化されます。
MEFから読み取るコントローラーファクトリを作成する
リクエストを処理する必要のあるコントローラーを検出するカスタムコントローラーファクトリを定義するためのコードは次のとおりです。
public class CustomControllerFactory : IControllerFactory
{
private readonly DefaultControllerFactory _defaultControllerFactory;
public CustomControllerFactory()
{
_defaultControllerFactory = new DefaultControllerFactory();
}
public IController CreateController(RequestContext requestContext, string controllerName)
{
var controller = Bootstrapper.GetInstance<IController>(controllerName);
if (controller == null)
throw new Exception("Controller not found!");
return controller;
}
public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
{
return SessionStateBehavior.Default;
}
public void ReleaseController(IController controller)
{
var disposableController = controller as IDisposable;
if (disposableController != null)
{
disposableController.Dispose();
}
}
}
さらに、各コントローラーには次のExport
属性を付ける必要があります。
[Export("Plugin1", typeof(IController))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class Plugin1Controller : Controller
{
public ActionResult Index()
{
return View();
}
}
Export
属性コンストラクターの最初のパラメーターは、コントラクト名を指定し、各コントローラーを一意に識別するため、一意である必要があります。PartCreationPolicy
コントローラは、複数の要求のために再利用することができないので、非共有に設定する必要があります。
プラグインからビューを見つけることを知っているビューエンジンを作成する
ビューエンジンは慣例によりViews
ホストアプリケーションのフォルダー内のビューのみを検索するため、カスタムビューエンジンの作成が必要です。プラグインは別のModules
フォルダーにあるため、ビューエンジンにもそこを探すように指示する必要があります。
public class CustomViewEngine : RazorViewEngine
{
private List<string> _plugins = new List<string>();
public CustomViewEngine(List<string> pluginFolders)
{
_plugins = pluginFolders;
ViewLocationFormats = GetViewLocations();
MasterLocationFormats = GetMasterLocations();
PartialViewLocationFormats = GetViewLocations();
}
public string[] GetViewLocations()
{
var views = new List<string>();
views.Add("~/Views/{1}/{0}.cshtml");
_plugins.ForEach(plugin =>
views.Add("~/Modules/" + plugin + "/Views/{1}/{0}.cshtml")
);
return views.ToArray();
}
public string[] GetMasterLocations()
{
var masterPages = new List<string>();
masterPages.Add("~/Views/Shared/{0}.cshtml");
_plugins.ForEach(plugin =>
masterPages.Add("~/Modules/" + plugin + "/Views/Shared/{0}.cshtml")
);
return masterPages.ToArray();
}
}
プラグインで強く型付けされたビューの問題を解決する
上記のコードのみを使用すると、モデルがbin
フォルダーの外部に存在するため、プラグイン(モジュール)で強く型付けされたビューを使用できませんでした。この問題を解決するには、次のリンクをたどってください。