问题
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