Unable to resolve Autofac dependencies from a Mef CompositionContainer when used according to documentation

1.3k views Asked by At

A Mef CompositionContainer is unable to resolve Autofac dependencies when used according to documentation at http://docs.autofac.org/en/latest/integration/mef.html.

I have a large code-base that has extensive use of a ServiceLocator and singletons... The service-locator does all object-creation, composition etc by using a cached System.ComponentModel.Composition.Hosting.CompositionContainer. (Also, note that we currently use/need metadata support) I am attempting to switch from this to a more modern architecture. For this to work, the existing Mef-based (‘CompositionContainer’-based) service-locator will have to cooperate with the Autofac IoC container.

The following function

  1. Creates a Mef CompositionContainer
  2. Demonstrates that MEF is able to resolve a simple export
  3. Configures Autofac to register with MEF and export the AutofacExport to MEF by using the .Exported extension method.
  4. Demonstrates that Autofac can resolve a mef-component with a dependency definend in Autofac
  5. Demonstrates that Mef is unable to resolve the exported component component with the Autofac dependency.

The exception thrown is: ImportCardinalityMismatchException("No exports were found that match the constraint: ContractName MefExportWithDependency RequiredTypeIdentity MefExportWithDependency" is thrown.

public void MefResolve_ObjectWithDependency_CanResolveWhenAutofacRegistersDependeyncy2()
{
    //1. Initialize Mef
    var composablePartCatalogs = new List<ComposablePartCatalog>
    {
        new AssemblyCatalog(Assembly.GetExecutingAssembly())
        //A lot more here..
    };

    var aggregateCatalog = new AggregateCatalog(composablePartCatalogs);
    var container = new CompositionContainer(aggregateCatalog, true);
    //2. As expected this is resolved
    container.GetExport<MefExport>().Should().NotBeNull();

    //3. Initialize Autofac
    var builder = new ContainerBuilder();
    builder.Register(c => new AutofacExport()).Exported(x => x.As<AutofacExport>());
    builder.RegisterComposablePartCatalog(aggregateCatalog);

    var ioc = builder.Build();

    //4. Here Autofac is correctly providing the dependency to the mef ImportingConstructor
    ioc.Resolve<MefExportWithDependency>().AutofacExport.Should().NotBeNull();

    //5. The next line will throw ImportCardinalityMismatchException
    container.GetExport<MefExportWithDependency>();
}

There code above expects the following classes to be defined:

public class AutofacExport { }

[Export]
public class MefExport { }

[Export]
public class MefExportWithDependency
{
    public AutofacExport AutofacExport { get; set; }

    [ImportingConstructor]
    public MefExportWithDependency(AutofacExport autofacExport)
    {
        AutofacExport = autofacExport;
    }
}

Note: I have also had a look at https://www.nuget.org/packages/MefContrib.Integration.Autofac/ - which promises to integrate Mef with Autofac. However, I am not able to find relevant documentation on how to configure that, and the package does not have a lot of usage.

1

There are 1 answers

4
Travis Illig On BEST ANSWER

I think you may have misunderstood the way the Autofac.Mef package is supposed to work. While it brings MEF items into Autofac, letting Autofac understand the export/import attributes and items registered in the MEF catalogs, it's a one-way operation - it doesn't push Autofac registrations into MEF or allow a MEF CompositionContainer to use Autofac.

I believe what you want is the MefContrib.Integration.Autofac package you mentioned, which appears to allow things to flow the other direction - from Autofac into MEF.

A good place to see how it works is in their repository, where they have some unit tests showing integration where MEF is resolving Autofac items. Here's one such test:

[Test]
public void ExportProviderResolvesServiceRegisteredByTypeTest()
{
    // Setup
    var builder = new ContainerBuilder();
    builder.RegisterType<AutofacOnlyComponent1>().As<IAutofacOnlyComponent>();
    var autofacContainer = builder.Build();
    var adapter = new AutofacContainerAdapter(autofacContainer);
    var provider = new ContainerExportProvider(adapter);

    var component = provider.GetExportedValue<IAutofacOnlyComponent>();
    Assert.That(component, Is.Not.Null);
    Assert.That(component.GetType(), Is.EqualTo(typeof(AutofacOnlyComponent1)));
}

As you can see, they have an adapter for Autofac containers that sort of "converts" them into something MEF can understand. They also have some unit tests showing bi-directional integration - MEF resolving from Autofac, Autofac resolving from MEF.

I've never personally used that package, but I think if you check out the tests and how they've got those set up it should get you on your way.