2011-12-14 2 views
1

Я хотел бы знать, как я могу импортировать импорт в свой пользовательский ExportProvider. Вот пример того, что я пытаюсь сделать:Удовлетворение импорта в пользовательском 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.

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

+0

Мы не можем отменить голоса. Но вы можете сделать это сами, теперь, когда он был отредактирован (да, я говорю о другом ответе) – NullUserException

ответ

2

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

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

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

EDIT:

// 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? Я не знаю ваших требований, но, возможно, вы можете обойтись без импорта.

+0

Есть ли проблемы с производительностью или памятью с вложенными контейнерами? – SonOfPirate

+0

Я бы не использовал вложенный контейнер, я отредактирую свой ответ на примере. – Gilles

+0

Но разве это не значит, что MEF должен пройти процесс «открытия» дважды? – SonOfPirate

1

В качестве альтернативы двойному решению 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 будет явно создать для вас, но и для всего остального, он должен работать.

Смежные вопросы