3

我正在使用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>会在查询其返回值时]。

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

4

1 回答 1

6

完成这项工作的关键是使用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());
}
于 2013-06-13T20:26:03.073 回答