Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom 'ExportFactory'

A desktop application using MEF imports many 'ServiceProviders'. Each part (ServiceProvider) is a class inside a separate DLL. All DLLs are in 'Plugin" folder which is used by desktop application.

Since I needed new Instances of my parts, ExportFactory was the best choice. The problem is my parts have constructors. I need pass some parameters into constructor of the part which is not supported by ExportFactory (MEF2, Preview2).

I need something like this:

// Each part has its own dependency
Dependency dependency = LoadDependency(myPart.Metedata["Name"]);
// Injecting dependency into part's constructor
myPart.CreateExport(dependency);

I don't want import anything from my parts-side.

A sample project (with parameter-less constructor) can be found here.

like image 940
Xaqron Avatar asked Dec 11 '10 15:12

Xaqron


1 Answers

When MEF sees an import of the type ExportFactory<IFoo>, it treats this in a special way. Instead of looking literally for an ExportFactory<IFoo> export, it looks instead for a IFoo export and magically generates a factory for that type.

Your mistake is that you expect this magic to also automatically work for your own alternative to ExportFactory which you called SrviceProviderFactory. This is not true. When you import SrviceProviderFactory<IFoo,IFooMetadata> somewhere, MEF literally looks for an export of that type.

The straightforward solution is to give it this export. Manually export a factory for each IServiceProvider implementation. For example, if you have a FooServiceProvider:

public class FooServiceProvider : IServiceProvider
{
    public FooServiceProvider(Dependency dependency)
    {
        ...
    }
}

Then you also need to have a FooServiceProviderFactory:

[Export(typeof(IServiceProviderFactory))]
[ExportMetaData("foo", "bar")]
public class FooServiceProviderFactory : IServiceProviderFactory
{
    public IServiceProvider CreateServiceProvider(Dependency d)
    {
       return new FooServiceProvider(d);
    }
}

And then your importer can select the right factory based on metadata:

public class FactoryUser
{
    [ImportMany]
    public Lazy<IServiceProviderFactory,IDictionary<string,object>>[] Factories 
    {
        get;
        set;
    }

    public void DoSomething()
    {
       var factory = Factories.First(x => x.Metadata["foo"] == "bar").Value;
       var serviceProvider = factory.CreateServiceProvider(someDependency);
       ...
    }
}

The annoying thing here is that for each service provider implementation, you also need to create and export a factory implementation. You can save work by creating a common factory base class (like your SrviceProviderFactory) but you still have to derive specific classes because you can't use generic type parameters in MEF exports. update: I believe .NET 4.5 now supports exporting open generic types.

That's why I already suggested you export Func instead, but apparently you didn't like that answer.

You could also try to replicate the ExportFactory magic. This is possible but a very advanced use case of MEF. If you want to do that I suggest you take a look at the MEF sources of ExportFactoryProvider to see how to build your own implementation with support for parameters.

like image 143
Wim Coenen Avatar answered Nov 07 '22 03:11

Wim Coenen