在MEF 2中使用闭合类型编写开放式通用类型

时间:2014-01-07 18:25:31

标签: c# generics mef open-generics mef2

据我所知,从MEF 2开始,MEF支持将开放泛型类型组合成封闭类型。我正在尝试从添加到同一组合容器的两个不同程序集中导出的类型中组合一个封闭类型,并且我收到一个ImportCardinalityMismatchException。我正在使用其中一个程序集的约定,因为它不在我的控制之下。另一方面,我使用了属性。

我不完全确定如何发表我的问题,因为我发现围绕泛型的术语相当令人困惑但是我想要在没有显式实现我自己的类的情况下编写我的新闭合类型,从Foo继承并提供它我的FooUser类型参数。我不知道这是否是我这样做的问题,或者是否与类型在不同的程序集中的事实有关。

在一个装配中,我有以下内容:

public class Foo<T> where T : Bar {}
public class Bar {}

在另一个集会中,我有以下内容:

[Export]
public class Bar2 : Bar {}

[Export]
public class Something
{
    [ImportingConstructor] 
    public Something([Import(typeof(Foo<>))] Foo<Bar2> foo) {}
}

在我的注册码中,我已完成以下操作:

var conventions = new RegistrationBuilder();
conventions.ForType(typeof(Foo<>)).Export();

var aggregateCatalog = new AggregateCatalog();
var catalog = new AssemblyCatalog(typeof(Foo<>).Assembly, conventions);
aggregateCatalog.Catalogs.Add(catalog);

catalog = new AssemblyCatalog(typeof(Something).Assembly);
aggregateCatalog.Catalogs.Add(catalog);

catalog = new AssemblyCatalog(typeof(Bar2).Assembly);
aggregateCatalog.Catalogs.Add(catalog);

var container = new CompositionContainer(aggregateCatalog, CompositionOptions.DisableSilentRejection);
var batch = new CompositionBatch();
batch.AddExportedValue(container);
container.Compose(batch);

后来我尝试导出我的价值:

container.GetExportedValue<Something>();

异常:抛出:“没有找到与约束匹配的导出:     ContractName Foo(Bar2)     RequiredTypeIdentity Foo(Bar2)“(System.ComponentModel.Composition.ImportCardinalityMismatchException) 抛出了System.ComponentModel.Composition.ImportCardinalityMismatchException:“未找到与约束匹配的导出:     ContractName Foo(Bar2)     RequiredTypeIdentity Foo(Bar2)“

我查看了我的约定实例,在容器中我有我的部分,即Foo {0},Bar2和Something。但是我仍然收到System.ComponentModel.Composition.ImportCardinalityMismatchException。

我已经在更抽象的情况下看到过这种情况,例如,哪里有一个IRepository,而不是哪里有更具体的东西,也没有跨越集合的项目。任何帮助将不胜感激。除非有任何有用的东西,否则我可能只会从有问题的类型中继承并完成它。

编辑:我刚刚构建了上面详细介绍的非常简单的示例,我实际上在实际项目中做的事情与我在这里不同,并且我的结果有所不同。我已经重命名了几种类型,使它们符合我的简化示例。

该组合物产生单一组成错误。根本原因如下。查看CompositionException.Errors属性以获取更多详细信息。

1)没有找到符合约束条件的导出:     ContractName CompositionTestLibrary.Foo(CompositionTestLibrary2.Bar2)     RequiredTypeIdentity CompositionTestLibrary.Foo(CompositionTestLibrary2.Bar2)

导致:无法在“CompositionTest.Something”部分设置导入'CompositionTest.Something..ctor(Parameter =“foo”,ContractName =“CompositionTestLibrary.Foo(CompositionTestLibrary2.Bar2)”)'。 元素:CompositionTest.Something..ctor(Parameter =“foo”,ContractName =“CompositionTestLibrary.Foo(CompositionTestLibrary2.Bar2)”) - &gt; CompositionTest.Something - &gt; AssemblyCatalog(Assembly =“CompositionTest,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = null”)

导致:无法从“CompositionTest.Something”部分导出'CompositionTest.Something(ContractName =“CompositionTest.Something”)'。 元素:CompositionTest.Something(ContractName =“CompositionTest.Something”) - &gt; CompositionTest.Something - &gt; AssemblyCatalog(Assembly =“CompositionTest,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = null”)

2 个答案:

答案 0 :(得分:1)

在以下行中,您不应使用conventions变量,因此您应该更改

catalog = new AssemblyCatalog(typeof(FooUser).Assembly, conventions);

catalog = new AssemblyCatalog(typeof(FooUser).Assembly);

在此处使用conventions实际上无法从定义FooUserSomething的程序集中导出任何内容,因此您将无法获得{{1}的组合值}。删除它将允许Something导出和编写。

答案 1 :(得分:0)

将约定传递给目录可防止MEF关闭具有类型约束的泛型类型。考虑以下类:

[Export]
public class Foo<T> where T : Bar { }
[Export]
public class FooUnconstrained<T> { }
[Export]
public class Bar { }

关闭不受约束通用类型 有效 ,无论是否传递了RegistrationBuilder

using (var catalogue = new ApplicationCatalog(new RegistrationBuilder()))
using (var container = new CompositionContainer(catalogue))
{
    Console.WriteLine(container.GetExport<FooUnconstrained<Bar>>().Value);
}

仅关闭受约束的通用类型 有效 没有RegistrationBuilder

using (var catalogue = new ApplicationCatalog())
using (var container = new CompositionContainer(catalogue))
{
    Console.WriteLine(container.GetExport<Foo<Bar>>().Value);
}

使用RegistrationBuilder 时,受约束的通用类型 失败

using (var catalogue = new ApplicationCatalog(new RegistrationBuilder()))
using (var container = new CompositionContainer(catalogue))
{
    // Next line throws ImportCardinalityMismatchException:
    Console.WriteLine(container.GetExport<Foo<Bar>>().Value);
}

这似乎是.net的错误。我在releaseKey = 528040(.net-4.8预览)上遇到此问题。

我个人还是建议反对传统模型,因为它要求合成者了解所有约定。这意味着您不能将两个使用不同约定的不同代码库放在一起,而无需手动组合它们的约定逻辑。也就是说,它引入了归因模型不存在的紧密耦合。