Reputation: 31463
I'm using MEF to discover and instantiate plugins and want to make the process as robust as possible. In particular I don't want a badly written plugin to adversely affect the execution of the host or other plugins.
Unfortunately I'm finding that when using GetExportedValues()
an exception thrown from any of the implementation class constructors effectively 'poisons' the container, preventing all 'good' implementations from being returned.
The following code demonstrates this:
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();
}
}
}
With the BadImplementation
throwing an exception from its constructor GetExportedValues()
throws an exception and therefore doesn't even return the good implementation. The error clearly states that the underlying problem is due to the BadImplementation
constructor:
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'.
...
[Note that GetExports()
fails similarly, but later, when its Lazy<IInterface>
return values are queried].
Is there any way I can work around this apparent limitation?
Upvotes: 3
Views: 801
Reputation: 31463
The key to making this work is to use GetExports()
instead of GetExportedValues()
. As noted within the question GetExports()
returns an IEnumerable<Lazy<T>>
. This can be iterated over and the implementations instantiated one at a time within a try-catch
block to check whether they are well behaved. If not then they can simply be ignored.
The following code demonstrates this and can replace the using
block in the example within the question:
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());
}
Upvotes: 6