8

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

合同
Web
BingTranslator
GoogleTranslator

合同包含ITranslate接口。顾名思义,它只包含合约(接口),因此出口商和进口商可以使用它。

public interface ITranslator
{
    string Translate(string text);
}

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";
    }
}

BingTranslator

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

现在,在我的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)
        });
    }
}

拼图的最后一块应该是将这些组件(部分)粘合在一起(从较小的部分组成整首歌曲)。因此,在Application_StartWeb项目中,我有:

        var parts = new AggregateCatalog
            (
                new DirectoryCatalog(Server.MapPath("/parts")), 
                new DirectoryCatalog(Server.MapPath("/bin"))
            );
        var composer = new CompositionContainer(parts);
        composer.ComposeParts();

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

4

3 回答 3

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)
        });
    }
}

我不是 MEF 方面的专家,坦率地说,我使用它的目的是什么,它对我没有多大作用,因为我只用它来加载 DLL,然后我有一个依赖注入的入口点,从那时起我使用 DI容器而不是 MEF。

MEF 势在必行——据我所知。在您的情况下,您需要主动编写您需要 MEFed 的内容,即您的控制器所以你的控制器工厂需要组成你的控制器实例。

由于我很少在我的 MVC 应用程序中使用 MEFed 组件,因此我为那些需要 MEF 的操作设置了一个过滤器(而不是在我的控制器工厂中使用 MEF 处理我的所有控制器):

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);
    }
}

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


我个人对 MEF 的看法

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

MEF 在运行时加载 DLL 方面非常强大,尤其是当您拥有 WPF 应用程序时,您可能正在加载/卸载插件并期望其他一切都按原样工作,添加/删除功能。

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

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

于 2012-05-22T11:00:10.453 回答
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 包,并且支持每个请求的生命周期

于 2012-05-22T15:10:15.020 回答
2

正如@Aliostad 所提到的,您确实需要在控制器创建期间/之后运行组合初始化代码才能使其工作 - 仅将其放在 global.asax 文件中是行不通的。

但是,您还需要使用[ImportMany]而不是 just [Import],因为在您的示例中,您可以使用您发现的二进制文件中的任意数量的 ITranslator 实现。关键是,如果您有很多ITranslator,但要将它们导入单个实例,您可能会从 MEF 获得异常,因为它不知道您真正想要哪种实现。

因此,您可以使用:

[ImportMany]
public IEnumerable<ITranslator> Translator { get; set; }

快速示例:

http://dotnetbyexample.blogspot.co.uk/2010/04/very-basic-mef-sample-using-importmany.html

于 2012-05-22T13:31:57.407 回答