Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom MEF ExportAttribute with AllowMultiple=true causes duplication

I want to export type Foo with multiple metadata options:

public interface IFoo
{
    void Do ();
}

[ExportFoo ("Bar", "1.0")]
[ExportFoo ("Baz", "1.0")]
[ExportFoo ("Baz", "2.0")]
public class Foo : IFoo
{
    public void Do () {}
}

I have declared ExportFooAttribute this way:

public interface IFooMeta
{
    string Name { get; }
    string Version { get; }
}

[MetadataAttribute, AttributeUsage (AttributeTargets.Class, AllowMultiple = true)]
public class ExportFooAttribute : ExportAttribute, IFooMeta
{
    public string Name { get; private set; }
    public string Version { get; private set; }

    public ExportFooAttribute (string name, string version) : base(typeof(IFoo))
    {
        Name = name;
        Version = version;
    }
}

According to documentation, when AllowMultiple is set to true, metadata actually contains arrays of properties of the original metadata, so I import types this way:

public interface IFooMultiMeta
{
    string[] Name { get; }
    string[] Version { get; }
}

public class Program
{
    [ImportMany]
    public List<Lazy<IFoo, IFooMultiMeta>> Foos { get; set; }

    private static void Main ()
    {
        new Program().MainInternal();
    }

    private void MainInternal ()
    {
        new CompositionContainer(new AssemblyCatalog(Assembly.GetExecutingAssembly())).ComposeParts(this);
        foreach (Lazy<IFoo, IFooMultiMeta> lazyFoo in Foos)
            for (int i = 0; i < lazyFoo.Metadata.Name.Length; i++)
                Console.WriteLine("* {0} {1}", lazyFoo.Metadata.Name[i], lazyFoo.Metadata.Version[i]);
        Console.WriteLine(Equals(Foos[0].Metadata, Foos[1].Metadata));
        Console.ReadKey();
    }
}

I expected to get one instance of Foo with metadata which contains arrays of 3 values. However, I got this:

* Baz 2.0
* Baz 1.0
* Bar 1.0
* Baz 2.0
* Baz 1.0
* Bar 1.0
* Baz 2.0
* Baz 1.0
* Bar 1.0
False

What's worse, metadata instances are different, so I can't even properly filter out duplicates.

Question: How to properly export one class as satisfying multiple combinations of metadata properties?

Complete sample: http://pastebin.com/WyjN95gr

like image 780
Athari Avatar asked Jan 14 '23 00:01

Athari


1 Answers

The reason for the three exports is the fact that you derive your custom export metadata from the ExportAttribute. This means that for each decoration a different export is taking place. Three decorations lead to three exports.

I'm not sure why you get all {Name,Version} pairs for each export.

To overcome the three exports you can update your custom attribute to derive from Attribute instead:

[MetadataAttribute, AttributeUsage (AttributeTargets.Class, AllowMultiple = true)]
public class ExportMetaFooAttribute : Attribute, IFooMeta
{
    public string Name { get; private set; }
    public string Version { get; private set; }

    public ExportFooAttribute (string name, string version)
    {
        Name = name;
        Version = version;
    }
}

I have renamed it to ExportMetaFooAttribute since it is not an export attribute but an export metadata attribute.

Then you change your Foo class to:

[Export(typeof(IFoo))]
[ExportMetaFoo("Bar", "1.0")]
[ExportMetaFoo("Baz", "1.0")]
[ExportMetaFoo("Baz", "2.0")]
public class Foo : IFoo
{
    public void Do ()
    {}
}

As you can see now we need the extra ExportAttribute to specify that this class needs to be exported.

like image 92
Panos Rontogiannis Avatar answered Jan 31 '23 00:01

Panos Rontogiannis