Как внедрить экспорт N MEF в 1 композитный сервис Unity, используя MefContrib или другой метод?

Скажем, у меня есть 5 отдельных сборок со следующим (предположим, что имя класса отличается в каждой):

[Export(typeof(IService))]
public class Service: IService
{
    // ...
}

И у меня есть класс, который будет составным из них в моей основной сборке.

public class CompositeService : IService
{
    public CompositeService(IEnumerable<IService> services)
    {
        // ...
    }
}

Я хотел бы, чтобы контейнер Unity разрешал CompositeService для IService и имел MefContrib для Unity, найдите 5 других экспортов и вставьте их в конструктор CompositeService.

Проблема в том, что у вас не может быть N экземпляров для безымянного unityContainer.RegisterType<IService> и не может быть для именованных экземпляров, если все они имеют одно и то же имя.

Я думаю, что мне не хватает чего-то простого в сочетании двух технологий (Unity + MEF) через третью (MefContrib), но я не могу понять, что это такое.

Возможно ли это или есть обходной путь? В конце концов, я перейду к полному двунаправленному внедрению зависимостей и динамическому обнаружению компонентов.


person Kit    schedule 06.10.2011    source источник
comment
Если вы экспортируете экземпляры службы, зачем вам регистрировать и создавать их экземпляры через Unity?   -  person Matthew Abbott    schedule 08.10.2011
comment
Мне нужно получить их в единстве, чтобы внедрить их в конструктор моего компонента единства, который принимает IEnumerable из них. Я могу сделать с ними container.RegisterInstance<T>, но им всем потребуются разные имена. Я играю с container.ResolveAll<T>, может поможет...   -  person Kit    schedule 10.10.2011


Ответы (2)


Я думаю, что, вероятно, лучший подход - перевернуть это. Вместо того, чтобы пытаться зарегистрировать свои компоненты через Unity, вы фактически оставляете обнаружение этих частей MEF. MEFContrib включает механизм интеграции Unity, который позволяет вставлять части, составленные из MEF, в компоненты Unity. Первоначально это было подробно описано в блоге Петра Водека, где он также дает вам образец. По сути, это работает так: вы можете использовать ряд методов расширения на вашем UnityContainer для регистрации ваших каталогов. Внутри он создаст соответствующее расширение и подключит ваш контейнер.

Вот быстрый и грязный пример, мы создадим несколько интерфейсов:

public interface IUnityComponent
{
  IEnumerable<IMefComponent> MefComponents { get; }
}

public interface IMefComponent
{
  string Name { get; }
}

А затем несколько образцов деталей, которые мы экспортируем (через MEF):

[Export(typeof(IMefComponent))]
public class MefComponent1 : IMefComponent
{
  public string Name { get { return "MEF Component 1"; } }
}

[Export(typeof(IMefComponent))]
public class MefComponent2 : IMefComponent
{
  public string Name { get { return "MEF Component 2"; } }
}

Теперь мы создадим еще одну часть (она будет создана через Unity):

public class UnityComponent : IUnityComponent
{
  public UnityComponent(IEnumerable<IMefComponent> mefComponents) 
  { 
    // mefComponents should be provided from your MEF container.
    MefComponents = mefComponents;
  }

  public IEnumerable<IMefComponent> MefComponents { get; private set; }
}

Чтобы все это подключить, нам просто нужно использовать метод расширения RegisterCatalog на вашем UnityContainer (импортируйте MefContrib.Integration.Unity после того, как вы добавили ссылку на MEFContrib):

var container = new UnityContainer();

// Register the catalog - this handles MEF integration.
container.RegisterCatalog(new DirectoryCatalog("."));

// Register our Unity components.
container.RegisterType<IUnityComponent, UnityComponent>(new ContainerControlledLifetimeManager());

Теперь вы сможете получить экземпляр и перечислить части, предоставляемые MEF:

// Grab an instance of our component.
var instance = container.Resolve<IUnityComponent>();
foreach (var mefComponent in instance.MefComponents)
{
  Console.WriteLine(mefComponent.Name);
}

примечание: 100 % не проверено.

person Matthew Abbott    schedule 10.10.2011
comment
Это дает IOE: Текущий тип System.Collections.Generic.IEnumerable`1[X.Extension.IXExtension] является интерфейсом и не может быть создан. Вам не хватает сопоставления типов?, одна из первых вещей, с которыми я столкнулся. - person Kit; 10.10.2011
comment
Можете ли вы изменить конструктор с IEnumerable<TradeSourceXTructed> на TradeSourceXTructed[]? - person Matthew Abbott; 10.10.2011
comment
Хм. Это создает и передает пустой массив. - person Kit; 10.10.2011

Только что попробовал то же решение от Мэтью здесь, и оно работает нормально, Unity собирает экспорт из MEF и внедряет их в конструктор (который принимает IEnumerable‹>).

Не знаю, может ли это вам помочь, но включение как MefContrib, так и MefContrib.Integration.Unity может помочь: какое-то время я включал только последний и сталкивался с похожими ошибками.

В качестве примечания: имейте в виду, что все регистрации в Unity (исходящие из экспорта MEF) будут «безымянными», поэтому, если вы попробуете ResolveAll‹>, вы получите пустую коллекцию, а если вы попробуете Resolve‹>, вы получите исключение. если зарегистрировано более 1 реализации.

person Tallmaris    schedule 21.12.2011