gpt4 book ai didi

c# - 基于MVC4 MEF的动态加载插件

转载 作者:行者123 更新时间:2023-12-03 14:25:13 27 4
gpt4 key购买 nike

已更新:请在这篇文章中阅读以下内容,以获取最小的解决方案

我对带有插件的MVC4解决方案有一些新手问题。我用谷歌搜索了一下,发现
一些好东西,但它不完全符合我的要求,因此我在这里要求一些建议。

在MVC中,类似于小部件的插件的最佳解决方案似乎是可移植区域(在MvcContrib包中)。我在这里找到了基本指导:

http://lostechies.com/erichexter/2009/11/01/asp-net-mvc-portable-areas-via-mvccontrib/

还有一些有用的提示:

http://geekswithblogs.net/michelotti/archive/2010/04/05/mvc-portable-areas-ndash-web-application-projects.aspx

这篇文章中的更多内容:

How to create ASP.NET MVC area as a plugin DLL?

太酷了,但可惜我的要求有些不同:

  • 不幸的是,我需要一个可以动态添加和发现插件的系统,而对于可移植区域,情况并非如此,必须由MVC主站点项目引用。我只想上传一些东西到网站上,让它发现并使用新的组件,因此我将为此使用MEF。
  • 幸运的是,我的插件将不像小部件,它们可能非常复杂且异构。相反,它们是必须遵循通用共享模式的组件。可以将它们视为专门的编辑器:对于每种数据类型,我将提供一个具有编辑功能的组件:新建,编辑,删除。因此,我想到了实现通用接口(interface)并提供诸如New,Edit,Delete之类的操作的插件 Controller 。
  • 我必须使用MVC4,将来我将不得不添加本地化和移动自定义。
  • 我必须避免来自复杂框架的依赖关系,并使代码尽可能简单。

  • 因此,每当我想在该网站中添加新的数据类型进行编辑时,我都想在其plugins文件夹中放置一个DLL,以存放逻辑内容( Controller 等)以及正确位置的一些 View ,以获取该网站发现并使用新的编辑器。

    最终,我可以在DLL本身中包含 View (我发现了这个: http://razorgenerator.codeplex.com和本教程: http://www.chrisvandesteeg.nl/2010/11/22/embedding-pre-compiled-razor-views-in-your-dll/,我想可以将其与Codeplex razorgenerator一起使用,因为它所指的代码与VS2012不兼容),但可能我最好将它们分开(也是由于本地化和移动意识要求);我当时正在考虑将一种上传机制添加到我的站点管理区域,在该区域中,您可以使用带有 Controller 和带有 View 的文件夹的DLL上传单个zip,然后让服务器在需要时解压缩并存储文件。这将使我可以轻松地修改 View ,而不必再次部署整个外接程序。

    因此,我开始寻找MEF和MVC,但是大多数文章都涉及MVC2,并且不兼容。我比较幸运的是,它主要专注于Web API,但是看起来很有前途并且很简单:

    http://kennytordeur.blogspot.it/2012/08/mef-in-aspnet-mvc-4-and-webapi.html

    这实质上将基于MEF的依赖项解析器和 Controller 工厂添加到“标准” MVC应用程序中。无论如何,帖子中的示例是指单组装解决方案,而我需要部署几个不同的插件。因此,我稍微修改了代码,以使用指向我的plugins文件夹的MEF DirectoryCatalog(而不是AssemblyCatalog),然后创建了一个测试MVC解决方案,并在类库中使用了一个插件。

    无论如何,当我尝试加载插件 Controller 时,框架会使用空类型调用我的工厂GetControllerInstance,因此,MEF当然无法进行合成。可能我遗漏了一些明显的东西,但是我是MVC 4的新手,欢迎任何建议或有用的链接(兼容MVC4)。谢谢!

    这是基本代码:

    公共(public)静态类MefConfig
    {
    公共(public)静态无效RegisterMef()
    {
    CompositionContainer容器= ConfigureContainer();

    ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory(container));

    System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver =
    新的MefDependencyResolver(container);
    }

    私有(private)静态CompositionContainer ConfigureContainer()
    {
    //AssemblyCatalog assemblyCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());

    DirectoryCatalog目录=新的DirectoryCatalog(
    HostingEnvironment.MapPath(“〜/Plugins”));
    CompositionContainer容器=新的CompositionContainer(catalog);
    返回容器;
    }
    }

    公共(public)类MefDependencyResolver:IDependencyResolver
    {
    私有(private)只读CompositionContainer _container;

    公共(public)MefDependencyResolver(CompositionContainer容器)
    {
    _container =容器;
    }

    公共(public)IDependencyScope BeginScope()
    {
    返回这个
    }

    公共(public)对象GetService(Type serviceType)
    {
    var export = _container.GetExports(serviceType,null,null).SingleOrDefault();
    return(export!= null?export.Value:null);
    }

    公共(public)IEnumerable GetServices(类型serviceType)
    {
    var export = _container.GetExports(serviceType,null,null);
    列出createdObjects = new List();

    如果(exports.Any())
    createdObjects.AddRange(exports.Select(export => export.Value));

    返回createdObjects;
    }

    公共(public)无效Dispose()
    {
    }
    }

    公共(public)类MefControllerFactory:DefaultControllerFactory
    {
    私有(private)只读CompositionContainer _compositionContainer;

    公共(public)MefControllerFactory(CompositionContainer compositionContainer)
    {
    _compositionContainer = compositionContainer;
    }

    protected 重写IController GetControllerInstance(
    System.Web.Routing.RequestContext requestContext,类型controllerType)
    {
    如果(controllerType == null)抛出新的ArgumentNullException(“controllerType”);
    var export = _compositionContainer.GetExports(controllerType,null,null).SingleOrDefault();

    IController结果;

    如果(null!=导出)结果= export.Value作为IController;
    别的
    {
    结果= base.GetControllerInstance(requestContext,controllerType);
    _compositionContainer.ComposeParts(result);
    }//eelse

    返回结果;
    }
    }

    您可以从此处下载完整的测试解决方案:

    http://www.filedropper.com/mvcplugins

    编辑:第一个可行的最小解决方案

    这是我的发现,希望它们对从此开始的其他新手有用:我没有成功运行上述答复中引用的框架,我想必须为VS2012和MVC4进行一些更新。无论如何,我看了一下代码,然后在Google上搜索了一下:

    1)首先,让我感到困惑的是同一个名称的两个不同接口(interface):IDependencyResolver。如果我了解得很好,则一个(System.Web.Http.Dependencies.IDependencyResolver)用于webapi,另一个(System.Web.Mvc.IDependencyResolver)用于通用DI。这篇文章对我有帮助: http://lucid-nonsense.co.uk/dependency-injection-web-api-and-mvc-4-rc/

    2),第三个组件是DefaultControllerFactory派生的 Controller 工厂,这对本文很重要,因为它是用于插件托管的 Controller 的工厂。

    这是我针对所有这些的实现,并从几个示例中进行了一些修改:首先是HTTP解析器:

    公共(public)密封类MefHttpDependencyResolver:IDependencyResolver
    {
    私有(private)只读CompositionContainer _container;

    公共(public)MefHttpDependencyResolver(CompositionContainer容器)
    {
    如果(container == null)抛出新的ArgumentNullException(“container”);
    _container =容器;
    }

    公共(public)对象GetService(Type serviceType)
    {
    如果(serviceType == null)抛出新的ArgumentNullException(“serviceType”);

    字符串名称= AttributedModelServices.GetContractName(serviceType);

    尝试
    {
    返回_container.GetExportedValue(name);
    }
    捕获
    {
    返回null;
    }
    }

    公共(public)IEnumerable GetServices(类型serviceType)
    {
    如果(serviceType == null)抛出新的ArgumentNullException(“serviceType”);

    字符串名称= AttributedModelServices.GetContractName(serviceType);

    尝试
    {
    返回_container.GetExportedValues(name);
    }
    捕获
    {
    返回null;
    }
    }

    公共(public)IDependencyScope BeginScope()
    {
    返回这个
    }

    公共(public)无效Dispose()
    {
    }
    }

    然后是MVC解析器,它非常相似,即使在这种情况下对于虚拟样本也完全没有必要:

    公共(public)类MefDependencyResolver:IDependencyResolver
    {
    私有(private)只读CompositionContainer _container;

    公共(public)MefDependencyResolver(CompositionContainer容器)
    {
    如果(container == null)抛出新的ArgumentNullException(“container”);
    _container =容器;
    }

    公共(public)对象GetService(Type type)
    {
    如果(type == null)抛出新的ArgumentNullException(“type”);

    字符串名称= AttributedModelServices.GetContractName(type);

    尝试
    {
    返回_container.GetExportedValue(name);
    }
    捕获
    {
    返回null;
    }
    }

    公共(public)IEnumerable GetServices(类型类型)
    {
    如果(type == null)抛出新的ArgumentNullException(“type”);

    字符串名称= AttributedModelServices.GetContractName(type);

    尝试
    {
    返回_container.GetExportedValues(name);
    }
    捕获
    {
    返回null;
    }
    }
    }

    最后是 Controller 工厂:

    [导出(typeof(IControllerFactory))
    公共(public)类MefControllerFactory:DefaultControllerFactory
    {
    私有(private)只读CompositionContainer _container;

    [导入构造函数]
    公共(public)MefControllerFactory(CompositionContainer容器)
    {
    如果(container == null)抛出新的ArgumentNullException(“container”);
    _container =容器;
    }

    公共(public)重写IController CreateController(RequestContext requestContext,字符串controllerName)
    {
    var controller = _container
    .GetExports()
    哪里(c => c.Metadata.Name.Equals(controllerName,StringComparison.OrdinalIgnoreCase))
    选择(c => c.Value)
    .FirstOrDefault();

    返回 Controller base.CreateController(requestContext,controllerName);
    }
    }

    至于示例 Controller ,我将其创建到类库项目中:

    [导出(typeof(IController))
    [PartCreationPolicy(CreationPolicy.NonShared)]
    [ExportMetadata(“Name”,“Alpha”)]
    公共(public)密封类AlphaController: Controller
    {
    公共(public)ActionResult Index()
    {
    ViewBag.Message =“你好,这是PLUGIN Controller !”;

    返回View();
    }
    }

    在主项目(MVC站点)中,我有一个Plugins文件夹,可在其中复制此DLL,并且在其文件夹中还包含此 Controller View 的“标准” View 集。

    这是最简单的情况,可能还有更多的要查找和完善的地方,但是我首先要很简单。无论如何,任何建议都是值得欢迎的。

    最佳答案

    我目前正在处理同一问题。我找到了这个解决方案:

  • 博客文章:http://blog.longle.net/2012/03/29/building-a-composite-mvc3-application-with-pluggable-areas/
  • 源代码:https://bitbucket.org/darincreason/mvcapplication8.web

  • 基本上,它将在Web应用程序启动时从指定位置以某种名称模式加载程序集:

    AssemblyInfo.cs:
    [assembly: PreApplicationStartMethod(
    typeof(PluginAreaBootstrapper), "Init")]

    PluginAreaBootstrapper.cs:
    public class PluginAreaBootstrapper
    {
    public static readonly List<Assembly> PluginAssemblies = new List<Assembly>();

    public static List<string> PluginNames()
    {
    return PluginAssemblies.Select(
    pluginAssembly => pluginAssembly.GetName().Name)
    .ToList();
    }

    public static void Init()
    {
    var fullPluginPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Areas");

    foreach (var file in Directory.EnumerateFiles(fullPluginPath, "*Plugin*.dll", SearchOption.AllDirectories))
    PluginAssemblies.Add(Assembly.LoadFile(file));

    PluginAssemblies.ForEach(BuildManager.AddReferencedAssembly);

    // Add assembly handler for strongly-typed view models
    AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolve;
    }

    private static Assembly AssemblyResolve(object sender, ResolveEventArgs resolveArgs)
    {
    var currentAssemblies = AppDomain.CurrentDomain.GetAssemblies();
    // Check we don't already have the assembly loaded
    foreach (var assembly in currentAssemblies)
    {
    if (assembly.FullName == resolveArgs.Name || assembly.GetName().Name == resolveArgs.Name)
    {
    return assembly;
    }
    }

    return null;
    }
    }

    但是我相信您可以创建一些目录观察器来动态加载程序集,因此您甚至不需要重新启动Web应用程序。

    我认为它可以满足您的1、2和4的需求。它非常简单,不需要任何框架,具有最少的配置,可以动态加载插件,并且可以与MVC 4一起使用。

    该解决方案将程序集插入Area目录,但是我相信您可以很容易地对其进行调整以使其像使用路由一样播放。

    关于c# - 基于MVC4 MEF的动态加载插件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12491904/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com