Satisfy Imports in custom ExportProvider

泪湿孤枕 提交于 2019-12-11 06:23:48

问题


I'd like to know how I can have Imports in my custom ExportProvider. Here's an example of what I'm trying to do:

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);
    }
}

I am adding the provider when I create the CompositionContainer.

Unfortunately, the import is never satisfied. I can see this by setting AllowDefaults = true so my provider is created, but _config is always null.

How can I configure the container and/or provider so the Import will be satisfied?


回答1:


When you are adding your export provider you are still creating your composition container. Thus I don't see how you can use the not yet created composition container to import parts of your custom export provider.

What I would do is first create a temporary CompositionContainer that will be used to create MyExportProvider.

Afterwards use the MyExportProvider to create your second final CompositionContainer that will be used by the rest of the application.

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);
        }
    }

You might also consider the problem from another angle. Do you really need to have imports in your custom ExportProvider? I do not know your requirements, but maybe you can make do without having imports.




回答2:


As an alternative to the dual CompositionContainer solution, you could wire this up in a single export provider, and have it compose itself using the same container. As an example, I've defined the following contract and it's export:

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

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

And with my example ExportProvider, I expect to be able to import an instance of it:

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>();
    }
}

I provide an instance of an ICompositionService, which CompositionContainer implements, and I perform a first-time initialisation when I call SetCompositionService. It checks to see if it has already been initialised, and if not, goes ahead and calls the SatisfyImportsOnce method on itself.

We would wire this up, something like this:

// 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);

Obviously you wouldn't be able to use any imports and your ExportProvider will explicitly create for you, but for everything else, it should work.



来源:https://stackoverflow.com/questions/8507242/satisfy-imports-in-custom-exportprovider

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!