如何使用MEF允许插件覆盖现有功能?

时间:2012-06-25 20:00:50

标签: c# mef

我正在使用MEF来允许用户扩展我的C#库。它到目前为止工作得很好,但是现在我正试图以我以前没见过的方式使用它。

到目前为止我见过的MEF的主要用例是:

  • 应用程序公开原始界面(IPerson
  • 外部库使用MEF和原始接口来扩展主库的功能(例如IPoliceman : IPerson,添加功能)
  • 应用程序然后使用ImportMany根据必须执行的操作搜索正确的IPerson

但我需要这样的事情:假设我有一个税收计算器,它接受一堆参数并根据这些参数返回估计税。我希望用户能够使用MEF创建修改完成这些计算的插件。应该只能在任何时候加载一个执行此操作的插件。否则,我如何决定使用哪种替代实现?

基本上,我的问题归结为:通常MEF允许添加类和方法的实现。如何使用它来允许用户替换实施?

3 个答案:

答案 0 :(得分:12)

通常,当您尝试覆盖应用程序中已存在的导出时,您将获得[Import(typeof(IFoo)]的基数异常,因为MEF预计只有一个匹配的导出可用。

但是,您可以将插件放在单独的导出提供程序中并赋予其优先级。在这里,我为应用程序文件夹中的“plugins”子文件夹执行此操作:

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;

传递给组合容器的导出提供程序的顺序决定了优先级:如果插件和应用程序部分都提供了导出,那么插件将获得优先级。

答案 1 :(得分:3)

你所谈论的实际上只是看待同一问题的另一种方式。答案比听起来简单 - 对于您希望客户端能够覆盖的任何行为,只需将该行为放入插件中。

没有任何内容表明你不能仅因为你是应用程序的作者而编写插件。将TaxCalculator类放在插件中,并公开一个界面,允许用户编写自己的税计算器。在运行时,如果您有多个已加载,请使用不属于您的那个。开箱即用,您将使用您的税务计算器插件,因此它将按照您期望的方式工作。如果用户创建自己的计税器插件并将其放在正确的目录中,则可以使用它,有效地允许它们“覆盖”您的原始功能。

答案 2 :(得分:1)

我不确定会有多大意义,但让我试试。

我会做一个TaxCalculatorManager课程。该类可以从MEF加载所有ITaxCalculator实现。从那里,您可以在Export属性中拥有允许对实现进行排名的内容。然后,当您需要计算税金时,您可以调用TaxCalculatorManager.Calculate来对ITaxCalculator个实施进行排名,并为获胜者调用Calculate

如果您需要我澄清任何要点,请告诉我。