I'm using MEF to allow users to extend my C# library. It's working great so far, but right now I'm trying to use it in a way I haven't seen it used before.
The primary use case for MEF I've seen so far is this:
IPerson
)IPoliceman : IPerson
, adds functionality)ImportMany
to search for correct IPerson
depending on what it must doBut I need something like this: Let's say I have a tax calculator that takes a bunch of parameters and returns estimated tax depending on those parameters. I want users to be able to create plugins with MEF that modify how those calculations are done. Only one plugin that does this should be able to be loaded at any one time. Otherwise, how do I decide which alternate implementation to use?
So basically, my question boils down to this: Usually MEF allows adding implementations of classes and methods. How do I use it to allow users to replace an implementation?
Normally when you try to override an export which is already present in the application, you will get a cardinality exception for an [Import(typeof(IFoo)]
because MEF expects exactly one matching export to be available.
However, you can put your plugins in a separate export provider and give it priority. Here I do that for a "plugins" subfolder inside the application folder:
Assembly executingAssembly = Assembly.GetExecutingAssembly();
string exeLocation = Path.GetDirectoryName(executingAssembly.Location);
string pluginPath = Path.Combine(exeLocation, "plugins");
var pluginCatalog = new DirectoryCatalog(pluginPath);
var pluginExportProvider = new CatalogExportProvider(pluginCatalog);
var appCatalog = new DirectoryCatalog(exeLocation,"*");
var appExportProvider = new CatalogExportProvider(appCatalog);
var container = new CompositionContainer(
pluginExportProvider, appExportProvider);
pluginExportProvider.SourceProvider = container;
appExportProvider.SourceProvider = container;
The order of the export providers as passed to the composition container determines the priority: if an export is provided by both the plugins and the application parts, then the plugins will get priority.
What you're talking about is actually just a different way of looking at the same problem. The answer is simpler than it sounds - for any behavior that you want a client to be able to override, just put that behavior in a plugin.
There's nothing that says you can't write plugins just because you're the author of the application. Put your TaxCalculator class in a plugin, and expose an interface allowing users to write their own tax calculators. At runtime, if you have more than one loaded, favor the one that isn't yours. Out-of-the-box, you will be using your tax calculator plugin, so it will work exactly the way you expect. If the user creates their own tax calculator plugin and drops it in the right directory, you use it instead, effectively allowing them to "override" your original functionality.
I'm not sure how much sense is going to make, but let me try.
I would make a TaxCalculatorManager
class. That class could load all of the ITaxCalculator
implementations from MEF. From there, you could have something in the Export
attribute that would allow ranking of the implementations. Then when you need to calculate the taxes, you would call TaxCalculatorManager.Calculate
which would rank the ITaxCalculator
implementations and call Calculate
on the winner.
Let me know if you need me to clarify any points.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With