How can you filter out parts in MEF2?

464 Views Asked by At

I'm trying to port some code from targeting the .NET Framework to .NET Core, and part of this involves switching from MEF1 to MEF2. There doesn't seem to be a great deal of documentation on using MEF in .NET Core, and I can't find any examples demonstrating how to filter out parts as was possible with MEF1.

In my original code (using MEF1), I wanted to load all parts exported from a set of assemblies except for MockCommunicationService. I implemented this as follows:

// Filter out invalid exports.
Func<ComposablePartDefinition, bool> partFilter = it => !it.ToString().Contains(nameof(MockCommunicationService));

var assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var catalog = new DirectoryCatalog(assemblyPath).Filter(partFilter);
var container = new CompositionContainer(catalog);

What would the equivalent to this be in MEF2? I imagine that it probably involves using ConventionBuilder, but I don't know if there's a way to define a convention along the lines of "permit everything except x".

Ideally, something like this would be great:

var conventions = new ConventionBuilder();
conventions.ForType<MockCommunicationService>().SuppressExports();

var configuration = new ContainerConfiguration()
    .WithAssemblies(assemblies, conventions);
1

There are 1 best solutions below

0
Tagc On

It's hardly an optimal solution, but this is the workaround I've used for the time being.

Looking at the source for ContainerConfiguration, I see that WithAssemblies is defined as:

public ContainerConfiguration WithAssemblies(IEnumerable<Assembly> assemblies, AttributedModelProvider conventions)
{
    if (assemblies == null) throw new ArgumentNullException(nameof(assemblies));
    return WithParts(assemblies.SelectMany(a => a.DefinedTypes.Select(dt => dt.AsType())), conventions);
}

So instead of using WithAssemblies, I use WithParts as follows:

// Filter out invalid exports.
Predicate<Type> filterParts = part => !part.Equals(typeof(MockCommunicationService));

var parts = from name in DependencyContext.Default.GetDefaultAssemblyNames()
            where name.Name.StartsWith("<<Company>>")
            let assembly = Assembly.Load(name)
            from definedType in assembly.DefinedTypes
            let part = definedType.AsType()
            where filterParts(part)
            select part;

var configuration = new ContainerConfiguration()
    .WithParts(parts);

return configuration.CreateContainer();

Again, this seems more like a hacky workaround than a proper way to go about it, so I'm not going to accept this answer. If no other answers get posted this may be useful to others, though.