在MEF中正确使用[Import]属性

Sae*_*ati 7 c# model-view-controller extensibility mef asp.net-mvc-3

我正在学习MEF,我想创建一个简单的例子(应用程序),看看它是如何运作的.因此我想到了一个简单的翻译.我创建了一个包含四个项目(DLL文件)的解决方案:

合同
Web
BingTranslator
GoogleTranslator

合同包含ITranslate界面.正如名称所适用的那样,它只包含合同(接口),因此出口商和进口商可以使用它.

public interface ITranslator
{
    string Translate(string text);
}
Run Code Online (Sandbox Code Playgroud)

BingTranslatorGoogleTranslator都是这份合同的出口商.他们都实施这份合同并提供(出口)不同的翻译服务(一个来自Bing,另一个来自Google).

[Export(typeof(ITranslator))]
public class GoogleTranslator: ITranslator
{
    public string Translate(string text)
    {
        // Here, I would connect to Google translate and do the work.
        return "Translated by Google Translator";
    }
}
Run Code Online (Sandbox Code Playgroud)

而且BingTranslator是:

[Export(typeof(ITranslator))]
public class BingTranslator : ITranslator
{
    public string Translate(string text)
    {
        return "Translated by Bing";
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,在我的Web项目中,我只想从用户那里获取文本,将其翻译成其中一个翻译者(Bing和Google),然后将结果返回给用户.因此,在我的Web应用程序中,我依赖于翻译.因此,我用这种方式创建了一个控制器:

public class GeneralController : Controller
{
    [Import]
    public ITranslator Translator { get; set; }

    public JsonResult Translate(string text)
    {
        return Json(new
        {
            source = text,
            translation = Translator.Translate(text)
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

拼图的最后一部分应该是将这些组件(部件)粘合在一起(从较小的部分组成整首歌曲).所以,在Application_Start对的网站项目,我有:

        var parts = new AggregateCatalog
            (
                new DirectoryCatalog(Server.MapPath("/parts")), 
                new DirectoryCatalog(Server.MapPath("/bin"))
            );
        var composer = new CompositionContainer(parts);
        composer.ComposeParts();
Run Code Online (Sandbox Code Playgroud)

其中/parts是我删除GoogleTranslator.dllBingTranslator.dll文件的文件夹(导出程序位于这些文件中),在/bin文件夹中我只有我的Web.dll文件,其中包含导入程序.但是,我的问题是,MEF不会使用所需的翻译器填充Translator属性GeneralController.我在这个网站上几乎阅读了与MEF相关的所有问题,但我无法弄清楚我的例子有什么问题.谁能告诉我我错过了什么?

Ali*_*tad 9

好的你需要做的是(没有规定性能,这只是为了看它工作)

public class GeneralController : Controller
{
    [Import]
    public ITranslator Translator { get; set; }

    public JsonResult Translate(string text)
    {
        var container = new CompositionContainer(
        new DirectoryCatalog(Path.Combine(HttpRuntime.BinDirectory, "Plugins")));
        CompositionBatch compositionBatch = new CompositionBatch();
        compositionBatch.AddPart(this);
        Container.Compose(compositionBatch);

        return Json(new
        {
            source = text,
            translation = Translator.Translate(text)
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

我不是MEF的专家,坦率地说我使用它,它对我没有太大帮助,因为我只使用它来加载DLL然后我有一个入口点依赖注入从那时起我使用DI容器而不是MEF.

就我所见,MEF势在必行.在您的情况下,您需要主动撰写您需要MEFed的内容,即您的控制器.所以你的控制器工厂需要组成你的控制器实例.

由于我很少在我的MVC应用程序中使用MEFed组件,因此我有一个过滤器用于需要MEF的操作(而不是MEFing我的控制器库中的所有控制器):

public class InitialisePluginsAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        CompositionBatch compositionBatch = new CompositionBatch();
        compositionBatch.AddPart(filterContext.Controller);
        UniversalCompositionContainer.Current.Container.Compose(
            compositionBatch);
        base.OnActionExecuting(filterContext);
    }
}
Run Code Online (Sandbox Code Playgroud)

UniversalCompositionContainer.Current.Container是一个用我的目录编目初始化的单例容器.


我个人对MEF的看法

MEF,虽然不是DI框架,但它做了很多.因此,与DI存在很大的重叠,如果您已经使用DI框架,它们必然会发生冲突.

MEF在运行时加载DLL的功能非常强大,尤其是当您有WPF应用程序时,您可能正在加载/卸载插件并期望其他所有内容都可以正常工作,添加/删除功能.

对于Web应用程序,这没有多大意义,因为您实际上不应该在工作的Web应用程序中删除DLL.因此,它的用途非常有限.

我将在ASP.NET MVC上写一篇关于插件的帖子,并将用链接更新这篇文章.

  • 好吧,我的车也飞不了.问题不在于你是否能够提出它不支持的花哨功能列表,而是它是否足够好.只有在有显着优势的情况下,我才会添加其他技术.MEF是.NET的一部分 - 所以它就在那里.任何其他的... questin不是"可以MEF做这个和那个"但是"这是否值得在技术堆栈中添加另一个项目". (4认同)
  • 嗯,这是您的观点的对应物 - MEF是.NET的一部分,本身就是一个非常好的DI框架.在大多数情况下,使用另一个是不合理的,只是引入了另一种没有增益的技术(即维护价值为负).刚刚使用MEF完成了18个月的项目;)工作得很好. (3认同)

Wim*_*nen 5

MEF将仅填充其构造自身的对象的导入.对于ASP.NET MVC,它是创建控制器对象的ASP.NET.它不会识别该[Import]属性,因此您就会看到缺少依赖项的原因.

要使MEF构建控制器,您必须执行以下操作:

  1. 使用标记控制器类本身[Export].
  2. 实现包装MEF容器的IDependencyResolver实现.您可以通过向MEF容器请求匹配的导出来实现GetService.您可以使用请求的类型生成MEF合同字符串AttributedModelServices.GetContractName.
  3. 通过调用DependencyResolver.SetResolver来注册该解析器Application_Start.

您可能还需要标记大部分导出的部分,[PartCreationPolicy(CreationPolicy.NonShared)]以防止同时在多个请求中重复使用同一个实例.保留在MEF部件中的任何州都将受到竞赛条件的限制.

编辑:这篇博文有一个很好的整个过程的例子.

edit2:可能还有其他问题.MEF容器将保存IDisposable对其创建的任何对象的引用,以便在容器本身被放置时它可以处置这些对象.但是,这不适用于具有"每个请求"生命周期的对象!对于任何实现的服务,您将有效地进行内存泄漏IDisposable.

可能更容易使用像AutoFac这样的替代方案,它具有用于ASP.NET MVC集成的NuGet包,并且支持每个请求的生命周期.