简体   繁体   English

使用约定在MEF 2中使用默认构造函数和开放泛型

[英]Default constructor and open generics in MEF 2 using conventions

I am trying to use MEF 2 in my project, I usually use SimpleInjector but this time I wanted to try MEF. 我正在尝试在项目中使用MEF 2,通常使用SimpleInjector,但是这次我想尝试MEF。 My main problem is dealing with open generics, this is what i got so far 我的主要问题是处理开放式泛型,这就是我到目前为止所得到的

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

Now for the container part I do 现在我要做容器部分

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 :(
}

When it's trying to retrieve the export I am getting this exception 当尝试检索导出时,出现此异常

No importing constructor was found on type 'MEFTest.Logger`1[System.Object]'. 在类型'MEFTest.Logger`1 [System.Object]'上未找到导入构造函数。

If I remove the constructor from the Logger class the container construct the closed generic just fine. 如果我从Logger类中删除构造函数,则容器构造封闭的泛型就可以了。 I am 98% sure that the problem is related with the constructors but I feel I am missing something here 我有98%的把握是问题与构造函数有关,但我觉得这里遗漏了一些东西

Edit 1: Doing some reading I have actually discovered that there are 2 versions of MEF, one that is a Nuget package and another one that ships with .NET40, the one that I am using is the Nuget package. 编辑1:做一些阅读,我实际上发现有2个版本的MEF,一个是Nuget软件包,另一个是.NET40附带的版本,我使用的是Nuget软件包。 I did some refactoring to use the one that ships with .NET40 我进行了一些重构以使用.NET40附带的组件

All the code is the same except for the part that creates and use the container 除了创建和使用容器的部分外,所有代码都是相同的

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 …
}

This works :) just fine so definitely I am missing something in the version of the Nuget package 这有效:)很好,所以我肯定在Nuget软件包的版本中缺少某些内容

Edit 2: With the removal of the "auto-detection" of the generic parts in the WithAssembly method the code works, here is the code refactored 编辑2:删除了WithAssembly方法中通用零件的“自动检测”功能后,代码起作用了,这里是重构的代码

The convention part: 约定部分:

var conventions = new ConventionBuilder();

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

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

The container part: 容器部分:

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 …
}

I changed the specific type to integer .When it executes Fetch() it returns correctly 0 as the default value for int 我将特定类型更改为integer 。当它执行Fetch()时,它会正确返回0作为int的默认值

The interesting part is why the "auto-detection" of the generics force the constructor to be marked 有趣的部分是为什么泛型的“自动检测”会强制标记构造函数

Edit 3: I think the "auto-detection" part is not the one at fault here because I have tried this 编辑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 …
}

With that code I am back to square one because it produces the same exception, it enforces the use of the marking attribute 有了该代码,我回到正题,因为它会产生相同的异常,它会强制使用marking属性

Edit 4: The actual MEF project has gone to the CoreFx GitHub page under System.Composition . 编辑4:实际的MEF项目已转到System.Composition下的CoreFx GitHub页面。 I went to the unit tests and in the RegistrationBuilderCompatibilityTest lines 40-58 我去了单元测试,并在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>>();
}

They never tested it without the default constructor 没有默认的构造函数,他们从未测试过它

Results: I end up opening an issue on the CoreFx Github page and submitted a PR with a fix for the bug 结果:我最终在CoreFx Github页面上打开了一个问题 ,并提交了带有该错误修复程序的PR

Try marking the constructor with the [ImportingConstructor] attribute. 尝试使用[ImportingConstructor]属性标记构造函数。

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

Until they merge my PR and release a version with the bug fixed I recommend going with the solution on Edit 2 . 在他们合并我的PR并发布修正了错误的版本之前,我建议使用Edit 2上的解决方案。 I think it is the least intrusive way of achieving the functionality required 我认为这是实现所需功能的最少介入方式

Basically you need to build conventions with your CLOSED generic types and create a collection with these closed generics. 基本上,您需要使用CLOSED泛型类型构建约定,并使用这些封闭的泛型创建集合。 In the container use the WithParts method along with the conventions that were defined, see Edit 2 for code snippet 在容器中,使用WithParts方法以及已定义的约定 ,有关代码段,请参见编辑2

UPDATE 更新

My PR has been merged and this functionality is now supported. 我的PR已合并,现在支持此功能。 It will be released in corefx 2.0 due by April 30, 2017 它将在2017年4月30日到期的corefx 2.0中发布

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM