Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MEF GetExportedValue with metadata

I want to use MEF as a DI for my project. I have 1 project and every classes that should be composed resides there (they share one interface). Now I want to create one of them by specifiyng a metadata value. Here's the definitions:

public interface IGatewayResponseReader
{
    object Read(string msg);
}

[Export(typeof(IGatewayResponseReader))]
[ExportMetadata(G3Reader.META_KEY, "value1")]
public class TestReader1 : IGatewayResponseReader
{
    ...
}

[Export(typeof(IGatewayResponseReader))]
[ExportMetadata(G3Reader.META_KEY, "value2")]
public class TestReader2 : IGatewayResponseReader
{
    ...
}

Now I want to create an instance of TestReader1 through MEF, but I don't know how to filter by metadata through CompositionContainer. I want something like

Container.GetExportedValue<IGatewayResponseReader>();

But to specify the metadata to choose which class instance to create.

Your help is much appreciated.

Thanks.

like image 474
Davita Avatar asked Aug 26 '11 11:08

Davita


1 Answers

The answer provied by @Dmitry Ornatsky is correct, but the preferred way of providing export metadata is to use strongly-typed metadata using a custom export attribute:

public interface IGatewayResponseReaderMetadata
{
    string Key { get; }
}

[MetadataAttribute]
[AttributeUsage( AttributeTargets.Class | AttributeTargets.Property )]
public class GatewayResponseReaderExportAttribute : ExportAttribute
{
    public GatewayResponseReaderExportAttribute( string key )
        : base( typeof( IGatewayResponseReader ) )
    {
        this.Key = key;
    }

    public string Key { get; set; }
}

[GatewayResponseReaderExport("value1")]
public class TestReader1 : IGatewayResponseReader
{
}

The code to look up an import can then be made type-safe. Note that it's a good idea to check if import is not null before accessing the Value property:

class Program
{
    [ImportMany]
    private List<Lazy<IGatewayResponseReader, IGatewayResponseReaderMetadata>> _readers;

    static void Main( string[] args )
    {
        CompositionContainer container = new CompositionContainer( new AssemblyCatalog( Assembly.GetExecutingAssembly() ) );

        Program program = new Program();
        container.SatisfyImportsOnce( program );


        var reader = program._readers.FirstOrDefault( r => r.Metadata.Key == "value1" );
        if ( reader != null )
            reader.Value.Read( ... );
    }
}
like image 124
Phil Devaney Avatar answered Sep 23 '22 03:09

Phil Devaney