【问题标题】:Default constructor and open generics in MEF 2 using conventions使用约定的 MEF 2 中的默认构造函数和开放泛型
【发布时间】:2016-12-12 08:36:36
【问题描述】:

我正在尝试在我的项目中使用 MEF 2,我通常使用 SimpleInjector,但这次我想尝试 MEF。我的主要问题是处理开放泛型,这就是我目前所得到的

public interface ISetting {}
public class Setting : ISetting {}

public interface ILogger<TLog>
{
    TLog Fetch()
}

public class Logger<TLog> : ILogger<TLog>
{
    private ISetting settings;

    public Logger(ISetting settings)
    {
        this.settings = settings;
    }

    public TLog Fetch()
    {
        return default(TLog);
    }
}

现在我要做的容器部分

var conventions = new ConventionBuilder();

conventions.ForType<Setting>()
           .Export<ISetting>()
           .Shared();

conventions.ForType(typeof(Logger<>))
           .Export(t => t.AsContractType(typeof(ILogger<>)))
           .Shared();

var configuration = new ContainerConfiguration()
        .WithAssembly(typeof(Program).Assembly,conventions);

using (var container = configuration.CreateContainer)
{
    var export = container.GetExport<ILogger<object>>(); //Exception :(
}

当它试图检索导出时,我得到了这个异常

在类型“MEFTest.Logger`1[System.Object]”上找不到导入构造函数。

如果我从 Logger 类中删除构造函数,则容器构造封闭的泛型就好了。我 98% 确定问题与构造函数有关,但我觉得我在这里遗漏了一些东西

编辑 1: 阅读我实际上发现有两个版本的 MEF,一个是 Nuget 包,另一个是 .NET40 附带的,我使用的是Nuget 包。我做了一些重构以使用 .NET40 附带的那个

除了创建和使用容器的部分之外,所有的代码都是一样的

var category = new AssemblyCatalog(Assembly.GetExecutingAssembly(), conventions);

using (var container = new CompositionContainer(category))
{
    var logic = container.GetExport<ILogger<int>>().Value; //Lazy Initialization O.o
    var test = logic.Fetch();

    // the rest of the code …
}

这行得通 :) 很好,所以我肯定在 Nuget 包的版本中遗漏了一些东西

编辑 2: 删除 WithAssembly 方法中通用部分的 “自动检测” 代码可以正常工作,这里代码重构了吗

约定部分:

var conventions = new ConventionBuilder();

conventions.ForType<Setting>()
           .Export<ISetting>();

conventions.ForType<Logger<int>>()
           .Export<ILogger<int>>();

容器部分:

var types = new Type[] { typeof(Setting), typeof(Logger<int>) };

var configuration = new ContainerConfiguration()
        .WithParts(types, conventions);

using (var container = configuration.CreateContainer())
{
    var logic = container.GetExport<ILogger<int>>();
    var test = logic.Fetch();

    // the rest of the code …
}

我将特定类型更改为 integer。当它执行 Fetch() 时,它会正确返回 0 作为 int 的默认值

有趣的部分是为什么泛型的“自动检测”强制构造函数被标记

编辑 3:我认为 “自动检测” 部分不是这里的问题,因为我已经尝试过了

var conventions = new ConventionBuilder();

conventions.ForType<Setting>()
           .Export<ISetting>();

conventions.ForType(typeof(Logger<>))
           .Export(t => t.AsContractType(typeof(ILogger<>)));

var types = new Type[] { typeof(Setting), typeof(Logger<>) };

var configuration = new ContainerConfiguration()
        .WithParts(types, conventions);

using (var container = configuration.CreateContainer())
{
    var logic = container.GetExport<ILogger<int>>();
    var test = logic.Fetch();

    // the rest of the code …
}

使用该代码我回到第一条,因为它产生相同的异常,它强制使用标记属性

编辑 4: 实际的 MEF 项目已转到 System.Composition 下的 CoreFx GitHub 页面。我参加了单元测试,并在 RegistrationBuilderCompatibilityTest 第 40-58 行

public interface IRepository<T> { }

public class EFRepository<T> : IRepository<T> { }

[Fact]
public void ConventionBuilderExportsOpenGenerics()
{
    var rb = new ConventionBuilder();

    rb.ForTypesDerivedFrom(typeof(IRepository<>))
      .Export(eb => eb.AsContractType(typeof(IRepository<>)));

    var c = new ContainerConfiguration()
        .WithPart(typeof(EFRepository<>), rb)
        .CreateContainer();

    var r = c.GetExport<IRepository<string>>();
}

他们从来没有在没有默认构造函数的情况下测试过它

结果:我最终在 CoreFx Github 页面上打开了一个 issue 并提交了一个 PR 并修复了该错误

【问题讨论】:

  • 我已经尝试过SelectConstructor(ctr =&gt; ctr.FirstOrDefault()) 并且不行。 顺便说一句,如果我从这一切中删除泛型,它就可以工作

标签: c# dependency-injection inversion-of-control mef


【解决方案1】:

尝试使用 [ImportingConstructor] 属性标记构造函数。

using System.Composition;
...
[ImportingConstructor]
public Logger(ISetting settings)
{
    this.settings = settings;
}

【讨论】:

  • 我想避免使用标记,因为如果我将此代码放入库中,我假设客户端将使用 MEF 来注入类。此外,如果我删除构造函数 MEF 将使用默认值,即使没有标记,标记也会出现问题。我不知道我解释得够不够清楚
  • 您的解决方案有效,根据 Prerequisite Imports 中的这篇文章 msdn.microsoft.com/en-us/library/ee155691(v=vs.110).aspx,它说 MEF 将选择默认构造函数并在未定义 [ImportingConstructor] 时抛出错误或者定义了多个 [ImportingConstructor],但正如我所说,这应该可以在没有使用约定的标记的情况下实现
【解决方案2】:

在他们合并我的 PR 并发布修复了错误的版本之前,我建议使用 Edit 2 上的解决方案。我认为这是实现所需功能的最少干扰方式

基本上,您需要使用您的 CLOSED 泛型类型建立约定,并使用这些封闭的泛型创建一个集合。在容器中使用 WithParts 方法以及定义的 约定,请参阅 Edit 2 了解代码 sn-p

更新

我的PR 已合并,现在支持此功能。它将于 2017 年 4 月 30 日在 corefx 2.0 中发布

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-29
    • 1970-01-01
    相关资源
    最近更新 更多