Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MEF and ABSTRACT FACTORY

UPDATE: Should I try to handle it by DI container or it's a wrong abstraction level here?

I would like to implement ABSTRACT FACTORY using MEF (.NET 4.5).

It doesn't work for me...

The composition remains unchanged. The changes were rejected because of the following error(s):

The composition produced a single composition error. The root cause is provided below. Review the CompositionException.Errors property for more detailed information.

1) No exports were found that match the constraint:

ContractName Mef3.Factory

RequiredTypeIdentity Mef3.Factory Resulting in: Cannot set import 'Mef3.Program._factory (ContractName="Mef3.Factory")' on part 'Mef3.Program'.

Element: Mef3.Program._factory (ContractName="Mef3.Factory") --> Mef3.Program

Is it the right way to do it in MEF? How can I forward id to Foo/Bar ctors?

The code:

class Program
{
    static void Main(string[] args)
    {
        var program = new Program();
        program.Run();
    }

    readonly CompositionContainer _container;

    public Program()
    {
        var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
        _container = new CompositionContainer(catalog);
        _container.ComposeParts(this);
    }

    [Import]
    public Factory _factory;

    public void Run()
    {
        var foo = _factory.GetInstance("foo", 123);
        Console.WriteLine(foo is Foo);
    }
}

[Export]
public class Factory
{
    private readonly ExportFactory<Foo> _fooFactory;
    private readonly ExportFactory<Bar> _barFactory;

    [ImportingConstructor]
    public Factory(ExportFactory<Foo> fooFactory, ExportFactory<Bar> barFactory)
    {
        _fooFactory = fooFactory;
        _barFactory = barFactory;
    }

    public Base GetInstance(string name, int id)
    {
        switch (name)
        {
            case "foo":
                return _fooFactory.CreateExport().Value;

            case "bar":
                return _barFactory.CreateExport().Value;
        }

        throw new ArgumentException();
    }
}

public class Foo : Base
{
    [ImportingConstructor]
    public Foo([Import("Id")] int id)
    {
    }
}

public class Bar : Base
{
    [ImportingConstructor]
    public Bar([Import("Id")] int id)
    {
    }
}

[InheritedExport]
public abstract class Base
{
}
like image 823
Dmitry Nogin Avatar asked Nov 12 '22 22:11

Dmitry Nogin


1 Answers

The issue looks to be caused by your [Import("Id")] on Foo and Bar. There is no export with a contract name of "Id". MEF in general doesn't allow you to pass in imports at runtime, you need to be able to satisfy the entire graph at composition time or else it will not work. If you wanted to use MEF to accomplish this particular scenario you should remove the ImportingConstructor on Foo and Bar and add a SetId method on the Base class and have your factory call it when you call GetInstance.

like image 148
Wes Haggard Avatar answered Nov 15 '22 13:11

Wes Haggard