在MEF 2中,通过使用封闭类型来组合开放式通用类型

4
我知道从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)") --> CompositionTest.Something --> AssemblyCatalog (Assembly="CompositionTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")

结果是:无法从部件“CompositionTest.Something”获取导出项'CompositionTest.Something (ContractName="CompositionTest.Something")'。 元素:CompositionTest.Something (ContractName="CompositionTest.Something") --> CompositionTest.Something --> AssemblyCatalog (Assembly="CompositionTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")


看起来对我来说没问题。也许你可以向我们展示一下如何创建目录并执行组合?另外,你确定你正在使用正确的程序集中的“conventions”变量,即定义了“Foo<T>”的那个程序集吗? - Adi Lester
我在我的真实代码中,就像上面的例子一样,将相同的RegistrationBuilder传递给每个目录。 - Robb Vandaveer
2个回答

1
在下一行中,您不应使用 conventions 变量,因此您需要进行更改。
catalog = new AssemblyCatalog(typeof(FooUser).Assembly, conventions);

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

在这里使用“约定”将有效地不从定义了FooUserSomething的程序集中导出任何内容,因此您将无法获得Something的组合值。删除它将允许Something被导出并组合。

不幸的是,情况并非如此。删除该参数没有任何作用。 - Robb Vandaveer

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 的一个 bug。我在 releaseKey=528040 (.net-4.8 preview) 上遇到了这种行为。

我个人不建议使用传统模型,因为它要求合成器了解所有惯例。这意味着您无法将使用不同惯例的两个不同代码库组合在一起,而不手动组合它们的惯例逻辑。也就是说,它引入了紧密耦合,而属性模型中不存在这种情况。


我已经在https://developercommunity.visualstudio.com/content/problem/615786/registrationbuilder-breaks-composition-of-unclosed.html上发布了此内容,它会破坏未关闭的组合。 - binki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接