I have IMessageSender interface.
using System.ComponentModel.Composition;
public interface IMessageSender
{
void Send(string message);
}
And I have two plugins that implements this interface. This is plugin.cs.
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;
using System;
[Export(typeof(IMessageSender))]
public class EmailSender : IMessageSender
{
public void Send(string message)
{
Console.WriteLine(message);
}
}
and this is plugin2.cs
[Export(typeof(IMessageSender))]
public class EmailSender : IMessageSender
{
public void Send(string message)
{
Console.WriteLine(message + "!!!!");
}
}
And I have this code to run those plugins with MEF.
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;
using System.Collections.Generic;
using System;
public class Program
{
[ImportMany]
public IEnumerable<IMessageSender> MessageSender { get; set; }
public static void Main(string[] args)
{
Program p = new Program();
p.Run();
foreach (var message in p.MessageSender) {
message.Send("hello, world");
}
}
public void Run()
{
Compose();
}
private void Compose()
{
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new DirectoryCatalog(@"./"));
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
}
}
After compilation, I get what I want.
> mono program.exe
hello, world
hello, world!!!!
My question is how can I selectively run out of many plugins. This example just gets all the available plugins to run all of them, but what should I do when I just want to run first plugin or second plugin?
For example, can I run only plugin2.dll as follows?
public static void Main(string[] args)
{
Program p = new Program();
p.Run();
var message = messageSender.GetPlugin("plugin"); // ???
message.Send("hello, world");
}
SOLVED
Based on this site, and Matthew Abbott’s answer. I could come up with this working code.
interface code (interface.cs)
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;
using System;
public interface IMessageSender
{
void Send(string message);
}
public interface IMessageSenderMetadata
{
string Name {get; }
string Version {get; }
}
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class MessageMetadataAttribute : ExportAttribute, IMessageSenderMetadata
{
public MessageMetadataAttribute( string name, string version)
: base(typeof(IMessageSender))
{
Name = name;
Version = version;
}
public string Name { get; set; }
public string Version { get; set; }
}
Plugin code (Plugin.cs …)
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;
using System;
[MessageMetadataAttribute("EmailSender1", "1.0.0.0")]
public class EmailSender : IMessageSender
{
public void Send(string message)
{
Console.WriteLine(message + "????");
}
}
Program.cs
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;
using System.Collections.Generic;
using System;
using System.Linq;
public class Program
{
[ImportMany(typeof(IMessageSender), AllowRecomposition = true)]
public IEnumerable<Lazy<IMessageSender, IMessageSenderMetadata>> Senders { get; set; }
public static void Main(string[] args)
{
Program p = new Program();
p.Run();
var sender1 = p.GetMessageSender("EmailSender1","1.0.0.0");
sender1.Send("hello, world");
sender1 = p.GetMessageSender("EmailSender2","1.0.0.0");
sender1.Send("hello, world");
}
public void Run()
{
Compose();
}
public IMessageSender GetMessageSender(string name, string version)
{
return Senders
.Where(l => l.Metadata.Name.Equals(name) && l.Metadata.Version.Equals(version))
.Select(l => l.Value)
.FirstOrDefault();
}
private void Compose()
{
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new DirectoryCatalog(@"./"));
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
}
}
MEF supports the exporting of custom metadata to accompany your exported types. What you need to do, is first define an interface that MEF will use to create a proxy object containing your metadata. In your example, you’ll likely need a unique name for each export, so we could define:
What you would then need to do, is make sure you assign that metadata for each of your exports that require it:
What MEF will do, is generate a project an implementation of your interface,
INameMetadatausing the value stored in theExportMetadata("Name", "EmailSender1")atrribute.After you’ve done that, you can do a little filtering, so redefine your
[Import]to something like:What MEF will create is an enumerable of
Lazy<T, TMetadata>instances which support deferred instantiation of your instance type. We can query as:Running this with an argument of
"EmailSender1"for thenameparameter will result in our instance ofEmailSenderbeing returned. The important thing to note is how we’ve selected a specific instance to use, based on querying the metadata associated with the type.You can go one further, and you could amalgamate the
ExportandExportMetadataattributes into a single attribute, such like:This allows us to use a single attribute to export a type, whilst still providing additional metadata:
Obviously querying this way presents you with a design decision. Using
Lazy<T, TMetadata>instances means that you’ll be able to defer instantiation of the instance, but that does mean that only one instance can be created per lazy. The Silverlight variant of the MEF framework also supports theExportFactory<T, TMetadata>type, which allows you to spin up new instances ofTeach time, whilist still providing you with the rich metadata mechanism.