Как импортировать конкретную деталь из нескольких деталей в MEF? - PullRequest
6 голосов
/ 06 января 2011

Я использую MEF в качестве DI-контейнера, и проблема в том, что я хочу импортировать конкретную деталь из нескольких частей.

Например, у меня есть следующие коды:

public interface IService
{
    void Send();
}

[Export(typeof(IService))]
public class Service : IService
{
    public void Send()
    {
        Console.WriteLine("Service.Send");
    }
}

[Export(typeof(IService))]
public class FakeService : IService
{
    public void Send()
    {
        Console.WriteLine("FakeService.Send");
    }
}

[Import]
public IService Service { get; set; } // ---> let's say I want to use FakeService

Есть ли решение?

Заранее спасибо

Ответы [ 3 ]

13 голосов
/ 07 января 2011

Вы можете экспортировать метаданные с вашим классом, вот пример:

public interface ILogger
{
  void Log(string message);
}

[Export(typeof(ILogger)), ExportMetadata("Name", "Console")]
public class ConsoleLogger : ILogger
{
  public void Log(string message)
  {
    Console.WriteLine(message);
  }
}

[Export(typeof(ILogger)), ExportMetadata("Name", "Debug")]
public class DebugLogger : ILogger
{
  public void Log(string message)
  {
    Debug.Print(message);
  }
}

Учитывая тот контракт и эти примеры реализации, мы можем импортировать типы как Lazy<T, TMetadata>, в результате чего мы можем определить контракт метаданных:

public interface INamedMetadata
{
  string Name { get; }
}

Вам не нужно беспокоиться о создании реализации метаданных, поскольку MEF будет проецировать любые значения атрибута ExportMetadata как конкретную реализацию TMetadata, которая в нашем примере равна INamedMetadata.С помощью вышеизложенного я могу создать следующий пример:

public class Logger
{
  [ImportMany]
  public IEnumerable<Lazy<ILogger, INamedMetadata>> Loggers { get; set; }

  public void Log(string name, string message)
  {
    var logger = GetLogger(name);
    if (logger == null)
      throw new ArgumentException("No logger exists with name = " + name);

    logger.Log(message);
  }

  private ILogger GetLogger(string name)
  {
    return Loggers
      .Where(l => l.Metadata.Name.Equals(name))
      .Select(l => l.Value)
      .FirstOrDefault();
  }
}

В этом примере класса я импортирую много экземпляров, например Lazy<ILogger, INamedMetadata>.Использование Lazy<T,TMetadata> позволяет нам получить доступ к метаданным до доступа к значению.В приведенном выше примере я использую аргумент name, чтобы выбрать соответствующий регистратор для использования.

Если создание экземпляра класса неправильно при импорте, вы можете использовать ExportFactory<T,TMetadata>, который позволяет вамраскручивать экземпляры ваших типов по требованию.(ExportFactory включено в версию Silverlight .NET 4.0, но Гленн Блок действительно выбросил исходный код в codeplex для настольных ПК / Web.

Надеюсь, это поможет.*

3 голосов
/ 06 января 2011

Вы можете использовать перегрузку экспорта, которая также принимает имя контракта. Затем импортируйте его с именем контракта.

[Export("Service", typeof(IService))]
public class Service : IService {
}

[Export("FakeService", typeof(IService))]
public class FakeService : IService {
}

[Import("FakeService")]
public IService Service { get; set; }
0 голосов
/ 06 января 2011

Вы можете добавить метаданные экспорта, чтобы отличать разные экспорты друг от друга. Затем на стороне импорта вам нужно будет использовать ImportMany, а затем отфильтровать его на основе метаданных, чтобы найти тот, который вы хотите.

...