如何防止“坏”实现中毒MEF中的GetExportedValues?

时间:2013-06-13 20:00:25

标签: .net mef

我正在使用MEF来发现和实例化插件,并希望尽可能使该过程更加健壮。特别是我不希望编写错误的插件对主机或其他插件的执行产生负面影响。

不幸的是,我发现当使用GetExportedValues()从任何实现类构造函数抛出的异常有效地“毒化”容器时,会阻止所有“好”实现被返回。

以下代码演示了这一点:

using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Linq;

namespace MefPoisoning {
    class Program {
        static void Main(string[] args) {
            var catalog = new TypeCatalog(
                typeof(GoodImplementation), 
                typeof(BadImplementation));

            using (var container = new CompositionContainer(catalog)) {
                try {
                    var implementations =
                        container.GetExportedValues<IInterface>();
                    Console.WriteLine("Found {0} implementations",
                        implementations.Count());
                }
                catch (CompositionException e) {
                    Console.WriteLine(e.Message);
                }
            }
        }
    }

    [InheritedExport]
    public interface IInterface {
    }

    public sealed class GoodImplementation : IInterface {
    }

    public sealed class BadImplementation : IInterface {
        public BadImplementation() {
            throw new InvalidOperationException();
        }
    }
}

BadImplementation从构造函数GetExportedValues()抛出异常会引发异常,因此甚至不会返回良好的实现。该错误清楚地表明潜在问题是由BadImplementation构造函数引起的:

The composition produced a single composition error. The root cause is 
provided below. Review the CompositionException.Errors property for more 
detailed information.

1) Operation is not valid due to the current state of the object.

Resulting in: An exception occurred while trying to create an instance 
of type 'MefPoisoning.BadImplementation'.

...

[请注意GetExports()同样失败,但稍后会查询其Lazy<IInterface>返回值。

有什么方法可以解决这个明显的限制吗?

1 个答案:

答案 0 :(得分:5)

使这项工作的关键是使用GetExports()而不是GetExportedValues()。如问题GetExports()中所述,会返回IEnumerable<Lazy<T>>。这可以迭代并且实现在try-catch块内一次一个地实例化以检查它们是否表现良好。如果没有,则可以简单地忽略它们。

以下代码演示了这一点,可以替换问题中示例中的using块:

using (var container = new CompositionContainer(catalog)) {
    var goodImplementations = new List<IInterface>();
    var lazyImplementations = container.GetExports<IInterface>();

    foreach (var lazyImplementation in lazyImplementations) {
        try {
            goodImplementations.Add(lazyImplementation.Value);
        }
        catch (CompositionException e) {
            // Failed to create implementation, ignore it
        }
    }

    Console.WriteLine("Found {0} implementations", goodImplementations.Count());
}