Удовлетворить импорт в пользовательских ExportProvider - PullRequest
1 голос
/ 14 декабря 2011

Я хотел бы знать, как я могу иметь Импорт в моем пользовательском ExportProvider.Вот пример того, что я пытаюсь сделать:

public class MyExportProvider : ExportProvider
{
    private List<Export> _exports;

    [Import()]
    private IConfig _config;

    public MyExportProvider()
       base()
    {
        _exports = new List<Export>();
    }

    protected override IEnumerable<Export> GetExportsCore(ImportDefinition definition,
                                                          AtomicComposition composition)
    {
        if (!_exports.Any())
            Initialize();

        return _exports.Where(x => definition.IsConstraintSatisfiedBy(s.Definition);
    }

    private void Initialize()
    {
        var contractName = typeof(MyObject).FullName;

        var exportDefinition = new ExportDefinition(contractName, null);
        var export = new Export(exportDefinition, () => new MyObject(_config));

        _exports.Add(export);
    }
}

Я добавляю провайдера при создании CompositionContainer.

К сожалению, импорт никогда не выполняется.Я могу убедиться в этом, установив AllowDefaults = true, чтобы мой провайдер был создан, но _config всегда имеет значение null.

Как настроить контейнер и / или провайдера, чтобы импорт был удовлетворен?

Ответы [ 2 ]

2 голосов
/ 21 декабря 2011

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

Сначала я создал бы временный CompositionContainer, который будет использоваться для создания MyExportProvider.

Впоследствии используйте MyExportProvider для создания вашего второго финального CompositionContainer, который будет использоваться остальной частью приложения.

РЕДАКТИРОВАТЬ:

    // this is your real container, only shown here for reference
    CompositionContainer container;

    public void BootstrapContainerMethod()
    {           
        // Replace this part with the catalogs required to create your export provider.
        var catalog = new AggregateCatalog();
        catalog.Catalogs.Add(new DirectoryCatalog("./bin", "*.dll")); 

        // Your temporary container, declared here in local scope
        // will be disposed because of using
        using (var bootstrapContainer = new CompositionContainer(catalog))
        {
            var myExportProvider = bootstrapContainer.GetExportedValue<IMyExportProvider>();

            // create your real container and optionnally add catalogs (not shown here)
            container = new CompositionContainer(myExportProvider);
        }
    }

Вы также можете рассмотреть проблему под другим углом.Вам действительно нужно импортировать данные в свой пользовательский ExportProvider?Я не знаю ваших требований, но, возможно, вы можете обойтись без импорта.

1 голос
/ 23 декабря 2011

В качестве альтернативы двойному решению CompositionContainer вы можете подключить его к одному поставщику экспорта и создать его с использованием одного и того же контейнера. В качестве примера я определил следующий контракт и его экспорт:

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

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

И с моим примером ExportProvider я ожидаю, что смогу импортировать его экземпляр:

public class TestExportProvider : ExportProvider
{
    private readonly object _lock = new object();
    private bool _initialised;

    [Import]
    public ILogger Logger { get; set; }

    public void SetCompositionService(ICompositionService service)
    {
        if (service == null) throw new ArgumentNullException("service");

        lock (_lock)
        {
            if (!_initialised)
            {
                InitialiseProvider(service);
            }
        }
    }

    private void InitialiseProvider(ICompositionService service)
    {
        service.SatisfyImportsOnce(this);
        _initialised = true;
    }

    protected override IEnumerable<Export> GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition)
    {
        if (_initialised)
        {
            Logger.Log("Getting available exports for '" + definition.ContractName + "'");

            // Do work here.);
            return Enumerable.Empty<Export>();
        }

        return Enumerable.Empty<Export>();
    }
}

Я предоставляю экземпляр ICompositionService, который реализует CompositionContainer, и я выполняю инициализацию впервые, когда я вызываю SetCompositionService. Он проверяет, была ли она уже инициализирована, и если нет, то запускает и вызывает метод SatisfyImportsOnce для себя.

Мы бы связали это, что-то вроде этого:

// Build our catalog.
var catalog = new AssemblyCatalog(typeof(Program).Assembly);

// Create our provider.
var provider = new TestExportProvider();

// Create our container.
var container = new CompositionContainer(catalog, provider);

// Register the composition service to satisfy it's own imports.
provider.SetCompositionService(container);

Очевидно, что вы не сможете использовать какой-либо импорт, и ваш ExportProvider явно создаст для вас, но для всего остального он должен работать.

...