问题
I'm use attribute-free approach to configuring MEF.
I'm read following articles:
http://msdn.microsoft.com/en-us/magazine/jj133818.aspx
http://blogs.microsoft.co.il/blogs/bnaya/archive/2013/01/12/mef-2-0-mini-series-part-4-fluent-import.aspx
Test code (console application project, .NET 4.5):
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Registration;
using System.Linq;
namespace MEF2
{
public interface IPlugin
{
void Run();
}
public interface IPluginMetadata
{
string Name { get; }
string Version { get; }
}
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
public class PluginMetadataAttribute : ExportAttribute, IPluginMetadata
{
public string Name { get; set; }
public string Version { get; set; }
public PluginMetadataAttribute(string name, string version)
: base(typeof(IPlugin))
{
Name = name;
Version = version;
}
}
[PluginMetadata("Plugin1", "1.0.0.0")]
public class Plugin1 : IPlugin
{
public void Run()
{
Console.WriteLine("Plugin1 runed");
}
}
[PluginMetadata("Plugin2", "2.0.0.0")]
public class Plugin2 : IPlugin
{
public void Run()
{
Console.WriteLine("Plugin2 runed");
}
}
class Program
{
CompositionContainer container;
IEnumerable<Lazy<IPlugin, IPluginMetadata>> plugins = Enumerable.Empty<Lazy<IPlugin, IPluginMetadata>>();
static void Main(string[] args)
{
var program = new Program();
foreach (var plugn in program.plugins) {
Console.WriteLine("{0}, {1}", plugn.Metadata.Name, plugn.Metadata.Version);
plugn.Value.Run();
}
}
Program()
{
var builder = new RegistrationBuilder();
builder
.ForTypesDerivedFrom<IPlugin>()
.Export<IPlugin>();
builder
.ForType<Program>()
.Export()
.ImportProperties<IPlugin>(
propertyFilter => true,
(propertyInfo, importBuilder) => {
importBuilder.AsMany();
}
);
var catalog = new AggregateCatalog(
new AssemblyCatalog(typeof(Program).Assembly, builder)
);
container = new CompositionContainer(catalog);
container.ComposeParts(this);
}
}
}
Exports works fine.
But when I try import many it doesn't work.
Please help me solve this problem.
回答1:
This is not an attribute free (imperative) approach since the export is attributed (declarative). Your custom metadata attribute derives from ExportAttribute
.
For this code to work you need to do the following:
- Remove the imperative export from the constructor code.
- Pass the registration builder to the
AssemblyCatalog
. Without this the registration builder cannot be used. - Update the convention-based import with
Lazy<IPlugin, IPluginMetadata>
since this is what is being exported. - Replace the call to
ComposeParts
withGetExports
.
The updated constructor code is:
var builder = new RegistrationBuilder();
builder
.ForType<Program>()
.Export()
.ImportProperties<Lazy<IPlugin, IPluginMetadata>>(
propertyFilter => true,
(propertyInfo, importBuilder) =>
{
importBuilder.AsMany();
}
);
var catalog = new AggregateCatalog(
new AssemblyCatalog(typeof(Program).Assembly, builder)
);
container = new CompositionContainer(catalog);
//container.ComposeParts(this);
plugins = container.GetExports<IPlugin, IPluginMetadata>();
I'm not sure how to make this work with ComposeParts
yet. I will have a look at it. Also the custom metadata does not have to derive from ExportAttribute
. It can derive from System.Attribute
. This will let you have a imperative export as well.
回答2:
Thanks, I found the solution for this problem.
Strange that I have never found such an example.
Sample code:
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Registration;
namespace MEF2
{
public interface IPlugin
{
void Run();
}
public interface IPluginMetadata
{
string Name { get; }
string Version { get; }
}
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
public class PluginMetadataAttribute : ExportAttribute, IPluginMetadata
{
public string Name { get; set; }
public string Version { get; set; }
public PluginMetadataAttribute(string name, string version)
: base(typeof(IPlugin))
{
Name = name;
Version = version;
}
}
[PluginMetadata("Plugin1", "1.0.0.0")]
public class Plugin1 : IPlugin
{
public void Run()
{
Console.WriteLine("Plugin1 runed");
}
}
[PluginMetadata("Plugin2", "2.0.0.0")]
public class Plugin2 : IPlugin
{
public void Run()
{
Console.WriteLine("Plugin2 runed");
}
}
public class PluginStore
{
public IEnumerable<Lazy<IPlugin, IPluginMetadata>> Plugins { get; private set; }
}
class Program
{
static void Main(string[] args)
{
var builder = new RegistrationBuilder();
builder
.ForTypesDerivedFrom<IPlugin>()
.Export<IPlugin>();
builder
.ForType<PluginStore>()
.Export()
.ImportProperties(
propertyFilter => true,
(propertyInfo, importBuilder) => {
importBuilder.AsMany();
}
);
var catalog = new AssemblyCatalog(typeof(PluginStore).Assembly, builder);
using (var container = new CompositionContainer(catalog)) {
var pluginStore = container.GetExport<PluginStore>().Value;
foreach (var plugin in pluginStore.Plugins) {
Console.WriteLine("{0}, {1}", plugin.Metadata.Name, plugin.Metadata.Version);
plugin.Value.Run();
}
}
}
}
}
来源:https://stackoverflow.com/questions/14376146/mef-2-import-many