Orchard CMSのようなプラグ可能なアーキテクチャでMVC4/MVC5アプリケーションを構築しようとしています。だから私は、スタートアッププロジェクトであり、認証、ナビゲーションなどの世話をするMVCアプリケーションを持っています。それから、asp.netクラスライブラリとして別々に構築された複数のモジュール、またはmvcプロジェクトを取り除いたコントローラ、ビュー、データリポジトリなどがあります.
私は一日中ウェブ上でチュートリアルを行ったり、サンプルなどをダウンロードしたりして、ケニーが最高の例を持っていることがわかりました- http://kennytordeur.blogspot.in/2012/08/mef-in-aspnet-mvc -4-and-webapi.html
これらのDLLへの参照を追加すると、モジュール(個別のDLL)からコントローラーをインポートできます。ただし、MEFを使用する理由は、実行時にモジュールを追加できることです。ビューと一緒にDLLをスタートアッププロジェクトの〜/ Modules //ディレクトリにコピーしたいので(これをなんとかできました)、MEFがそれらを取得します。 MEFがこれらのライブラリをロードするのに苦労しています。
この回答で説明されているように、MefContribもあります ASP.NET MVC 4.0コントローラーとMEF、これら2つをどのように統合するか これは私が次にしようとしていることです。しかし、MEFがMVCですぐに動作しないことに驚いています。
(MefContribの有無にかかわらず)同様のアーキテクチャが動作している人はいますか?当初、Orchard CMSを削除してフレームワークとして使用することを考えていましたが、複雑すぎます。また、WebAPI2を活用するためにMVC5でアプリを開発できたら嬉しいです。
私は、あなたが説明したものと同様のプラグ可能なアーキテクチャを持ち、同じ技術_ASP.NET MVC
_とMEF
を使用したプロジェクトに取り組みました。認証、承認、およびすべての要求を処理するホストASP.NET MVCアプリケーションがありました。プラグイン(モジュール)は、そのサブフォルダーにコピーされました。プラグインは、独自のモデル、コントローラー、ビュー、css、jsファイルを持つ_ASP.NET MVC
_アプリケーションでもありました。これらを機能させるために行った手順は次のとおりです。
MEFのセットアップ
アプリケーションの起動時に構成可能なすべてのパーツを検出し、構成可能なパーツのカタログを作成するMEF
に基づいてエンジンを作成しました。これは、アプリケーションの起動時に一度だけ実行されるタスクです。エンジンは、すべてのプラグ可能なパーツを検出する必要があります。この場合、ホストアプリケーションの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
{
//
// GET: /Plugin1/
public ActionResult Index()
{
return View();
}
}
_
Export
属性コンストラクターの最初のパラメーターは、コントラクト名を指定し、各コントローラーを一意に識別するため、一意でなければなりません。コントローラーを複数の要求に再利用できないため、PartCreationPolicy
をNonSharedに設定する必要があります。
プラグインからビューを見つけることを知っているビューエンジンを作成する
慣例により、ビューエンジンはホストアプリケーションの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
フォルダーの外部に存在するため、プラグイン(モジュール)で強く型付けされたビューを使用できません。この問題を解決するには、次の link に従ってください。
MEFのコンテナには、作成するIDisposableオブジェクトへの参照を保持する「素敵な機能」があり、巨大なメモリリークが発生することに注意してください。伝えられるところでは、メモリリークはこのnugetで対処できます- http://nuget.org/packages/NCode.Composition.DisposableParts.Signed
プラグインアーキテクチャを実装するプロジェクトがあります。これらのいずれかを使用するか、ソースコードを見て、これらのことをどのように達成するかを確認することをお勧めします。
また、 外部アセンブリのコントローラーの404 は興味深いアプローチを取っています。質問を読むだけで多くのことを学びました。