MEF: передача различных параметров конструктора детали при использовании CreationPolicy.NonShared - PullRequest
9 голосов
/ 29 марта 2012

Я знаю, что было много вопросов относительно ввода параметров конструктора с использованием MEF, но у меня это немного другое.

Я хочу знать, есть ли способ передать различные значения параметров конструктору детали, когда я использую комбинацию PartCreationPolicy(CreationPolicy.NonShared) и GetExportedValue?

Например:

[PartCreationPolicy(CreationPolicy.NonShared)]
[Export]
public partial class Foo
{
    [ImportingConstructor]
    public Foo([Import("SomeParam")]object parameter)
    {
        ...
    }
}

и где-то еще ...

container.ComposeExportedValue("SomeParam", "Some value...");
var instance = container.GetExportedValue<Foo>();

В приведенном выше примере я могу использовать ComposeExportedValue только один раз, так как при его повторном запуске ChangeRejectedException.

Итак, мои вопросы:

  1. Есть ли другой способ изменить значение SomeParam в приведенном выше сценарии для каждого нового экземпляра?
  2. Если нет, то как еще можно это сделать без использования какой-либо другой структуры DI? Одна вещь, которая приходит на ум, - это создать сервис для предоставления чего-то вроде System.Collections.Concurrent.ConcurrentQueue, где я помещаю значение параметра в очередь перед вызовом GetExportedValue, а затем снимаю значение в конструкторе детали. Но это взлом, а также создает больше проблем, чем решает.
  3. Если ответ на оба вышеупомянутых вопроса - нет, то есть ли другие способы сделать это с помощью комбинации MEF и некоторой другой структуры DI / IOC?

Спасибо за любую помощь. :)
С уважением,
Йогеш Ягота

Ответы [ 2 ]

2 голосов
/ 30 марта 2012

Если ответ на оба вышеупомянутых вопроса - нет, то есть ли другие способы сделать это с помощью комбинации MEF и некоторой другой структуры DI / IOC?

Я думаю, что ответ на вопрос 1 и 2 действительно нет.

Я бы попробовал AutoFac , который дает вам более точный контроль и интегрируется с MEF . Например, он позволяет вам настроить регистрацию таким образом, чтобы экземпляры Bar и Baz получали свой экземпляр Foo с другим параметром:

builder.Register(c => new Bar(new Foo(param));
builder.Register(c => new Baz(new Foo(param2));
1 голос
/ 01 июля 2014

Если вы хотите использовать разные экземпляры одного и того же интерфейса в зависимости от некоторой логики (применить шаблон стратегии) ​​в MEF, один из способов использовать атрибут ExportMetadata. Например, если у вас есть IDbManager и если у вас есть две реализации, скажем, один Oracle и один Sql, то 1. Создайте интерфейс метаданных, который будет содержать метаданные

public interface IDbManagerMetadata
{
    DataProvider DataProvider { get;  }
}

2. Создайте класс атрибутов, как показано ниже

[MetadataAttribute]
public class DbManagerMetadataAttribute : Attribute, IDbManagerMetadata
{
    public DataProvider DataProvider { get; set; }
}
  1. Пример стратегии

    публичное перечисление DataProvider { Oracle, Sql, } [InheritedExport] открытый интерфейс IDbManager { void Initialize (); }

    [InheritedExport (TypeOf (IDbManager))] открытый класс DbManager: IDbManager { public DbManager (DataProvider providerType) { _providerType = providerType; }

    public void Initialize()
    {
        Console.WriteLine("provider : {0}", _providerType);
    }
    
    public DataProvider _providerType { get; set; }
    

    }

И две разные реализации

[Export(typeof(IDbManager))]
[DbManagerMetadata(DataProvider = DataProvider.Oracle)]
public sealed class OracleDataProvider : DbManager
{
    public OracleDataProvider():base(DataProvider.Oracle)
    {

    }
}

и

[Export(typeof(IDbManager))]
[DbManagerMetadata(DataProvider = DataProvider.Sql)]
public sealed class SqlDataProvider : DbManager
{
    public SqlDataProvider()
        : base(DataProvider.Sql)
    {
    }
}

И вы можете решить, какой из них использовать, используя интерфейс метаданных, который мы создали на первом шаге, как показано в репозитории, показанном ниже

[Export]
public class Repository
{
    private IDbManager _dbManager;

    private readonly IEnumerable<Lazy<IDbManager, IDbManagerMetadata>> DbManagers;

    [ImportingConstructor]
    public Repository([ImportMany(typeof(IDbManager))]IEnumerable<Lazy<IDbManager, IDbManagerMetadata>> dbManagers)
    {
        this.DbManagers = dbManagers;
        var _dbManager = DbManagers.First(x => x.Metadata.DataProvider == DataProvider.Oracle).Value;
    }

    public void Execute()
    {
        var oracleDbManager = DbManagers.First(x => x.Metadata.DataProvider == DataProvider.Oracle).Value;

        oracleDbManager.Initialize();

        var sqlDbManager = DbManagers.First(x => x.Metadata.DataProvider == DataProvider.Sql).Value;

        sqlDbManager.Initialize();
    }
}
...