问题
I want to make my plugin subsystem using mef but i have few question because i am newbie in csharp and mef (
What i want to do:
- every plugin can create its own interface IPlugin1, IPlugin2 ...
- each of these interfaces must have specified functions Load, Unload
- using mef i want to enum all exports and call Load/Unload
The questions:
a. Is my solution good and if no how can i improve it (by using something else)?
b. How to enum all exports using mef and call specified interface function?
I would appreciate for all links and critics. Thanks for all.
回答1:
Building on @m-y's answer, I do think having a common interface is the best design you can go with. It is the easiest way to leverage a common set of operations you could apply to your plugins. What I would consider though, is a slight refinement:
public interface IPlugin : IDisposable
{
void Initialise();
}
By enforcing the Dispose
method is implemented, you part can automatically be controlled by the lifetime management features of the CompositionContainer
. All your unloading code can go in there, here is a sample plugin:
public interface ILogger : IPlugin
{
void Log(string message);
}
[Export(typeof(ILogger))]
public class ConsoleLogger : ILogger
{
void IPlugin.Initialise()
{
Console.WriteLine("Initialising plugin...");
}
public void Log(string message)
{
Console.WriteLine(message);
}
public virtual void Dispose(bool disposing)
{
if (disposing)
{
Console.WriteLine("Disposing plugin...");
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
The Dispose
pattern allows a standardised mechanism for cleaning up your code. Your CompositionContainer
instance will keep track of this item, and clean it up when it is disposed.
Now, what I wanted to describe is a nice addition in MEFContrib called the InterceptingCatalog. What this catalog allows you to do, is register interception strategies which could give you access to an exported value before it is returned to your calling code. One such use could be to automatically ensure that Initialise
is called on the base IPlugin
interface when the instance is being exported for the first time:
public class InitialisePluginStrategy : IExportedValueInterceptor
{
public object Intercept(object value)
{
var plugin = value as IPlugin;
if (plugin != null)
plugin.Initialise();
return value;
}
}
Let's tie all this up:
static void Main(string[] args)
{
var catalog = new AssemblyCatalog(typeof(Program).Assembly);
var configuration = new InterceptionConfiguration()
.AddInterceptor(new InitialisePluginStrategy());
var interceptingCatalog = new InterceptingCatalog(catalog, configuration);
var container = new CompositionContainer(interceptingCatalog);
var logger = container.GetExportedValue<ILogger>();
logger.Log("test");
Console.ReadKey();
}
If you run that, you'll notice our ConsoleLogger
is automatically initialised the first time we get it from the container. We don't need to worry about it being initialised again, it will only do so when it is creating the exported instance, which means it obeys singleton and non-singleton scenarios.
You might be thinking at this point that this could be overkill, but its actually quite an elegant solution to enable your parts to be automatically started, and disposed of when no longer needed.
Of course, if you want fine grained control over how your parts are initialised, you could just manage these in a method you write yourself, you just need to consider the plugin state when you do so.
You can read up about the InterceptingCatalog more at Piotr Włodek's blog
回答2:
If you want all your plugin interfaces to adhere to an interface of their own then you should provide an interface for them to extend:
public interface IMyInterface
{
void Load();
void Unload();
}
Then, when the plugs create their own interfaces, they should extend from your publicly provided interface:
public interface IPlugin1 : IMyInterface
{
void DoPlugin1Func();
}
Now, in order to get a collection of interfaces that are exported in MEF you'd have to tag your interface as an InheritedExport:
[InheritedExport(typeof(IMyInterface))]
public interface IMyInterface { ... }
This states that any class that somehow extends from the IMyInterface will get exported as a type of IMyInterface, even down the tree:
//This class will be exported as an IMyInterface
public class PluginImplementation1 : IPlugin1 { ... }
Finally, somewhere in your code (where applicable), you can import a collection of IMyInterface instances.
public class SomeClass
{
[ImportMany(typeof(IMyInterface))]
private IEnumerable<IMyInterface> Plugins { get; set; }
}
If everything is wired up properly, Plugins
will be an enumeration of classes that, through inheritance, exported out as IMyInterface
s.
Resources:
- InheritedExport Example
- ImportMany Example
来源:https://stackoverflow.com/questions/7388115/mef-enum-exports-and-architecture