MEF 2 中使用约定的默认构造函数和开放泛型
Default constructor and open generics in MEF 2 using conventions
我正在尝试在我的项目中使用 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 :(
}
当它试图检索导出时我遇到了这个异常
No importing constructor was found on type 'MEFTest.Logger`1[System.Object]'.
如果我从 Logger class 中删除构造函数,容器就可以很好地构造封闭的泛型。我 98% 确定问题与构造函数有关,但我觉得我在这里遗漏了一些东西
编辑 1: 通过阅读,我实际上发现有 2 个版本的 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 中通用部分的 "auto-detection" 代码工作的方法,这里是重构的代码
约定部分:
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 …
}
我把具体的类型改成了整数。当它执行Fetch()时,它returns正确地默认为0整数值
有趣的是为什么泛型的"auto-detection"强制构造函数被标记
编辑 3: 我认为 "auto-detection" 部分不是问题所在,因为我已经试过了
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项目已经转到CoreFx GitHub页面System.Composition。我进行了单元测试,并在 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>>();
}
他们从未在没有默认构造函数的情况下对其进行测试
结果: 我最终打开了一个 issue on the CoreFx Github page and submitted a PR 修复了错误
尝试使用 [ImportingConstructor] 属性标记构造函数。
using System.Composition;
...
[ImportingConstructor]
public Logger(ISetting settings)
{
this.settings = settings;
}
直到他们合并我的 PR 并发布修复了错误的版本,我建议使用 Edit 2 上的解决方案。我认为这是实现所需功能的最少干扰方式
基本上,您需要使用 CLOSED 泛型建立约定,并使用这些封闭泛型创建一个集合。在容器中使用 WithParts 方法以及已定义的 conventions,代码请参见 Edit 2片段
更新
我的 PR 已合并,现在支持此功能。它将在 corefx 2.0 中发布,截止日期为 2017 年 4 月 30 日
我正在尝试在我的项目中使用 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 :(
}
当它试图检索导出时我遇到了这个异常
No importing constructor was found on type 'MEFTest.Logger`1[System.Object]'.
如果我从 Logger class 中删除构造函数,容器就可以很好地构造封闭的泛型。我 98% 确定问题与构造函数有关,但我觉得我在这里遗漏了一些东西
编辑 1: 通过阅读,我实际上发现有 2 个版本的 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 中通用部分的 "auto-detection" 代码工作的方法,这里是重构的代码
约定部分:
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 …
}
我把具体的类型改成了整数。当它执行Fetch()时,它returns正确地默认为0整数值
有趣的是为什么泛型的"auto-detection"强制构造函数被标记
编辑 3: 我认为 "auto-detection" 部分不是问题所在,因为我已经试过了
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项目已经转到CoreFx GitHub页面System.Composition。我进行了单元测试,并在 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>>();
}
他们从未在没有默认构造函数的情况下对其进行测试
结果: 我最终打开了一个 issue on the CoreFx Github page and submitted a PR 修复了错误
尝试使用 [ImportingConstructor] 属性标记构造函数。
using System.Composition;
...
[ImportingConstructor]
public Logger(ISetting settings)
{
this.settings = settings;
}
直到他们合并我的 PR 并发布修复了错误的版本,我建议使用 Edit 2 上的解决方案。我认为这是实现所需功能的最少干扰方式
基本上,您需要使用 CLOSED 泛型建立约定,并使用这些封闭泛型创建一个集合。在容器中使用 WithParts 方法以及已定义的 conventions,代码请参见 Edit 2片段
更新
我的 PR 已合并,现在支持此功能。它将在 corefx 2.0 中发布,截止日期为 2017 年 4 月 30 日