Составление открытых универсальных типов с закрытыми типами в MEF 2

Я понимаю, что начиная с 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..ctor (Parameter = "foo", ContractName = "CompositionTestLibrary.Foo (CompositionTestLibrary2.Bar2)")' в части 'CompositionTest.Something'. Элемент: CompositionTest.Something..ctor (Parameter = "foo", ContractName = "CompositionTestLibrary.Foo (CompositionTestLibrary2.Bar2)") -> CompositionTest.Something -> AssemblyCatalog (Assembly = "CompositionTest, Version = 1.0.0.0, Культура = нейтральный, PublicKeyToken = null ")

Результат: Невозможно получить экспорт 'CompositionTest.Something (ContractName = "CompositionTest.Something")' из части 'CompositionTest.Something'. Элемент: CompositionTest.Something (ContractName = "CompositionTest.Something") -> CompositionTest.Something -> AssemblyCatalog (Assembly = "CompositionTest, Version = 1.0.0.0, Culture = нейтральный, PublicKeyToken = null")


person Robb Vandaveer    schedule 07.01.2014    source источник
comment
Мне кажется, это нормально. Может быть, покажете, как вы составляете каталог и исполняете композицию? Кроме того, уверены ли вы, что используете переменную conventions с правильной сборкой, то есть той, в которой определено Foo<T>?   -  person Adi Lester    schedule 07.01.2014
comment
Я. В моем реальном коде, как и в приведенном выше примере, я передаю один и тот же RegistrationBuilder каждому каталогу.   -  person Robb Vandaveer    schedule 07.01.2014


Ответы (2)


В следующей строке вы не должны использовать переменную conventions, поэтому вам следует изменить

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

to

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

Использование здесь conventions фактически не будет экспортировать ничего из сборки, в которой определены FooUser и Something, поэтому вы не сможете получить составное значение Something. Его удаление позволит экспортировать и составить Something.

person Adi Lester    schedule 07.01.2014
comment
К сожалению, это не так. Удаление этого параметра ничего не делает. - person Robb Vandaveer; 07.01.2014

Передача соглашений в каталог не позволяет 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).

Я лично не рекомендую использовать обычную модель, потому что она требует, чтобы наборщик знал обо всех условных обозначениях. Это означает, что вы не можете объединить две разные кодовые базы, использующие разные соглашения, без объединения их логики соглашений вручную. То есть он вводит тесную связь, которой нет в атрибутивной модели.

person binki    schedule 20.06.2019
comment
Я разместил это на developercommunity.visualstudio. ru / content / problem / 615786 / - person binki; 20.06.2019