首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >采用MVC 4或5插拔架构的MEF (2014)

采用MVC 4或5插拔架构的MEF (2014)
EN

Stack Overflow用户
提问于 2014-01-09 18:04:55
回答 3查看 43.6K关注 0票数 80

我正在尝试构建一个具有可插拔架构的MVC4/MVC5应用程序,比如Orchard CMS。所以我有一个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加载这些库。

还有这个答案ASP.NET MVC 4.0 Controllers and MEF, how to bring these two together?中解释的MefContrib,这是我接下来要尝试的东西。但令我惊讶的是,MEF不能开箱即用MVC。

有没有人有类似的架构(使用或不使用MefContrib)?最初,我甚至想过剥离Orchard CMS并将其用作框架,但它太复杂了。也将很好地开发在MVC5中的应用程序,以利用WebAPI2。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2014-01-10 13:49:08

我曾经参与过一个项目,它与您描述的那个项目具有类似的可插拔架构,并且它使用了相同的技术ASP.NET、MVC和MEF。我们有一个主机ASP.NET MVC应用程序来处理身份验证、授权和所有请求。我们的插件(模块)被复制到它的子文件夹中。这些插件也是具有自己的模型、控制器、视图、css和js文件的ASP.NET MVC应用程序。我们遵循了以下步骤来使其正常工作:

设置MEF

我们创建了基于MEF的引擎,它在应用程序启动时发现所有可组合部件,并创建可组合部件的目录。这是一个仅在应用程序启动时执行一次的任务。引擎需要发现所有可插拔部件,在我们的示例中,这些部件位于主机应用程序的bin文件夹或Modules(Plugins)文件夹中。

代码语言:javascript
复制
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部件发现的类的示例代码。从Global.asax.cs文件中的Application_Start方法调用类的Compose方法。为了简单起见,减少了代码。

代码语言:javascript
复制
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读取的控制器工厂的

下面是定义自定义控制器工厂的代码,它将发现需要处理请求的控制器:

代码语言:javascript
复制
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属性:

代码语言:javascript
复制
[Export("Plugin1", typeof(IController))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class Plugin1Controller : Controller
{
    //
    // GET: /Plugin1/
    public ActionResult Index()
    {
        return View();
    }
}

Export属性构造函数的第一个参数必须是唯一的,因为它指定协定名称并唯一标识每个控制器。PartCreationPolicy必须设置为NonShared,因为控制器不能重复用于多个请求。

创建知道从插件中查找视图的视图引擎

需要创建自定义视图引擎,因为按照惯例,视图引擎只在宿主应用程序的Views文件夹中查找视图。由于插件位于单独的Modules文件夹中,我们需要告诉视图引擎也在那里查看。

代码语言:javascript
复制
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

票数 105
EN

Stack Overflow用户

发布于 2016-03-23 15:55:32

请注意,MEF的容器有一个“很好的特性”,可以保留对它创建的任何IDisposable对象的引用,这将导致巨大的内存泄漏。据说内存泄漏可以通过这个nuget - http://nuget.org/packages/NCode.Composition.DisposableParts.Signed来解决

票数 4
EN

Stack Overflow用户

发布于 2015-10-20 22:21:15

有一些项目实现了插件架构。您可能想要使用其中一个,或者查看它们的源代码,看看它们是如何完成这些事情的:

  • ASP.NET MVC Plugin Framework (使用MVC 4)
  • .NET 4.0 ASP.NET MVC 3 plug-in architecture with embedded views (显然使用mvc3,但基本原则可能仍然适用)

此外,404 on Controllers in External Assemblies正在采取一种有趣的方法。通过阅读问题,我学到了很多。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/21017036

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档