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ブロック内で一度に 1 つずつインスタンス化され、動作が適切かどうかを確認できます。そうでない場合は、単純に無視できます。

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 に答える