到目前为止,我在 MEF 方面遇到的最大问题是,当我在插件加载器包装器中编写部件时,当它发现其中一个程序集存在导入解析问题时,加载会完全失败。 理想情况下,我希望 ComposeParts 表现出某种“忽略并继续”行为,因为理想的用户体验需要加载尽可能多的插件,并在特定插件加载失败时简单地记录事件。 我在任何地方的文档中都找不到有关此内容的信息。
如果您对如何解决此问题有任何其他建议,我会倾听!
Wim 的示例具有基本思想,但我建议您不要直接拉动容器,而是像这样进行 Lazy ImportMany:
[Export]
public class MyApplication
{
[ImportMany(typeof(IPlugin))]
public IEnumerable<Lazy<IPlugin>> Plugins { get; set; }
}
然后您可以一一初始化插件并捕获其中的任何错误,例如:
void InitializePlugins()
{
foreach (Lazy<IPlugin> plugin in Plugins)
{
try
{
plugin.Value.Initialize();
}
catch (CompositionException e)
{
// Handle the error accordingly
}
}
}
在您第一次拉取 .Value 之前,不会创建实际的插件,如果插件在导入的构造函数或属性设置器中存在错误,则会发生错误。另请注意,如果插件出现错误,我会捕获 CompositionException,这是 .Value 调用中会出现的情况。
您可以使用
AllowDefault
参数。如果没有可用部分可以满足导入,则在导入时将其设置为 true 将导致依赖关系为 null
。
public class MyComponent
{
[Import(AllowDefault=true)]
public IMyDependency MyDependency { get; set; }
}
要加载所有可用的插件,但忽略那些因缺少部分而无法加载的插件,
[ImportMany]
默认情况下已经执行了您想要的操作:
[Export]
public class MyApplication
{
[ImportMany(typeof(IPlugin))]
public IEnumerable<IPlugin> Plugins { get; set; }
}
请注意,上述技术只能消除由于缺少零件而导致的构图错误。如果一个部件及其导入实际上可用,但在调用构造函数时抛出意外异常,那么您仍然会收到异常。要忽略此类与组合无关的问题,您可以像这样直接调用容器:
IEnumerable<IPlugin> GetPluginsFromContainer(CompositionContainer container)
{
foreach (Lazy<IPlugin> pluginExport in container.GetExports<IPlugin>())
{
try
{
yield return pluginExport.Value;
}
catch (Exception e)
{
// report failure to instantiate plugin somewhere
}
}
}
我对这个问题的解决方案是为每个插件调用
ComposeParts
并保留已完成的插件列表。单独加载所有插件后,使用列表中的所有插件创建一个新的 AggregateCatalog
并在该目录上使用 ComposeParts:
string[] pluginDirectories = Directory.GetDirectories(pluginsFolder);
List<string> workinglugins = new List<string>();
foreach (string pluginDirectory in pluginDirectories)
{
Trace.WriteLine(String.Format("Discovering Plugins from '{0}'.", pluginDirectory));
try
{
// Try loading each Plugin one by one
DirectoryCatalog catalog = new DirectoryCatalog(pluginDirectory, "*Plugin.dll");
CompositionContainer container = new CompositionContainer(catalog);
container.ComposeParts(this);
// Plugin worked
workinglugins.Add(pluginDirectory);
}
catch (Exception ex)
{
// Plugin didn't work
Trace.WriteLine(String.Format("Exception loading Plugin: {0}", ex.Message));
}
}
if (workinglugins.Count > 0)
{
// Create the catalog that will be used for the final composition
AggregateCatalog catalog = new AggregateCatalog();
foreach (string plugin in workinglugins)
{
try
{
catalog.Catalogs.Add(new DirectoryCatalog(plugin, "*Plugin.dll"));
}
catch (Exception ex)
{
Trace.WriteLine(String.Format("Exception loading Plugin: {0}", ex.Message));
}
}
// Create the CompositionContainer with the parts in the catalog.
try
{
_container = new CompositionContainer(catalog);
_container.ComposeParts(this);
Trace.WriteLine(String.Format("{0} plugins succesfully loaded.", _container.Catalog.Count()));
}
catch (Exception ex)
{
Trace.WriteLine(String.Format("Exception loading Plugins: {0}", ex.Message));
}
}
else
{
Trace.WriteLine(String.Format("No Plugins discovered."));
}