Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/323.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 具有MVC4或5的MEF-可插拔架构(2014年)_C#_Asp.net_.net_Asp.net Mvc_Mef - Fatal编程技术网

C# 具有MVC4或5的MEF-可插拔架构(2014年)

C# 具有MVC4或5的MEF-可插拔架构(2014年),c#,asp.net,.net,asp.net-mvc,mef,C#,Asp.net,.net,Asp.net Mvc,Mef,我正在尝试构建一个MVC4/MVC5应用程序,该应用程序具有可插拔的体系结构,如Orchard CMS。因此,我有一个MVC应用程序,它将作为启动项目,负责身份验证、导航等。然后将有多个模块分别构建为asp.net类库或精简的MVC项目,并具有控制器、视图、数据报告等 我花了一整天的时间在网上浏览教程,下载样本等,发现肯尼有最好的例子- 如果我添加对这些DLL的引用,我可以从模块(单独的DLL)导入控制器。但是使用MEF背后的原因是能够在运行时添加模块。我希望DLL和视图一起被复制到启动项目中的

我正在尝试构建一个MVC4/MVC5应用程序,该应用程序具有可插拔的体系结构,如Orchard CMS。因此,我有一个MVC应用程序,它将作为启动项目,负责身份验证、导航等。然后将有多个模块分别构建为asp.net类库或精简的MVC项目,并具有控制器、视图、数据报告等

我花了一整天的时间在网上浏览教程,下载样本等,发现肯尼有最好的例子-

如果我添加对这些DLL的引用,我可以从模块(单独的DLL)导入控制器。但是使用MEF背后的原因是能够在运行时添加模块。我希望DLL和视图一起被复制到启动项目中的~/Modules//目录中(我已经成功地做到了),MEF只需将它们捡起来。努力让MEF加载这些库

正如在这个答案中所解释的,还有MefContrib,这是我下一步要尝试的。但是我很惊讶MEF不能与MVC一起开箱即用


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

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

设置MEF

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

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;
    }
}
假设所有插件都复制到位于主机应用程序根目录中的
Modules
文件夹的单独子文件夹中。每个插件子文件夹包含
视图
子文件夹和每个插件的DLL。在上面的
应用程序\u 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
属性构造函数的第一个参数必须是唯一的,因为它指定合同名称并唯一标识每个控制器。
PartCreationPolicy
必须设置为非共享,因为控制器不能用于多个请求

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

需要创建自定义视图引擎,因为视图引擎按约定仅在主机应用程序的
视图
文件夹中查找视图。由于插件位于单独的
模块
文件夹中,因此我们需要通知视图引擎也在那里查看

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();
    }
}
公共类CustomViewEngine:RazorViewEngine
{
私有列表_plugins=新列表();
公共CustomViewEngine(列出插件文件夹)
{
_插件=插件文件夹;
ViewLocationFormats=GetViewLocations();
MasterLocationFormats=GetMasterLocations();
PartialViewLocationFormats=GetViewLocations();
}
公共字符串[]GetViewLocations()
{
var views=新列表();
添加(“~/views/{1}/{0}.cshtml”);
_plugins.ForEach(plugin=>
添加(“~/Modules/“+plugin+”/views/{1}/{0}.cshtml”)
);
返回视图。ToArray();
}
公共字符串[]GetMasterLocations()
{
var masterPages=新列表();
添加(“~/Views/Shared/{0}.cshtml”);
_plugins.ForEach(plugin=>
添加(“~/Modules/“+plugin+”/Views/Shared/{0}.cshtml”)
);
返回母版页。ToArray();
}
}
使用插件中的强类型视图解决问题


由于只使用上述代码,我们无法在插件(模块)中使用强类型视图,因为模型存在于
bin
文件夹之外。要解决这个问题,请遵循以下步骤。

有一些项目实现了插件体系结构。您可能希望使用其中一个或查看其源代码,以了解它们是如何完成这些任务的:

  • (使用MVC4)
  • (显然使用MVC 3,但基本原则可能仍然适用)

此外,该公司正在采取一种有趣的方法。通过阅读这个问题,我学到了很多。

请注意,MEF的容器有一个“很好的功能”,它保留对它创建的任何IDisposable对象的引用,并将导致巨大的内存泄漏。据称,内存泄漏可以通过此nuget解决-

每个模块的自定义路由如何?我认为每个需要获取routetable和global asax的ref的模块都应该有路由接口,其中路由接口将同时显示在模块文件夹和核心中。我们通过为每个插件定义单独的区域来解决这个问题。在每个插件中,我们创建了一个继承自AreaRegistration的类,通过重写RegisterArea方法,我们能够定义我们希望在插件中使用的路由。您是否有此解决方案的示例项目?我同意cpoDesign。一个样本项目会更好,我也同意样本p
[Export("Plugin1", typeof(IController))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class Plugin1Controller : Controller
{
    //
    // GET: /Plugin1/
    public ActionResult Index()
    {
        return View();
    }
}
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();
    }
}