Combining metadata and export in same attribute

Dec 3, 2009 at 2:35 PM

Is this a supported scenario? I.e. combining metadata and export in the same attribute. The test succeds when metadata is a separate attribute.

[MetadataAttribute]
public class MyExportAttribute : ExportAttribute, IMeta
{
    public MyExportAttribute() : base(typeof(IService)) {}

    public string Meta { get; set; }
}

public interface IMeta
{
    string Meta { get; }
}

public interface IService
{
}

[MyExportAttribute(Meta = "Hello")]
public class Service
{
}

public class Client
{
    [ImportMany(typeof(IService))]
    public IEnumerable<Lazy<IService, IMeta>> Services { get; set; }
}

[TestClass]
public class MetadataMefAssertions
{
    [TestMethod]
    public void CanReadInstancesAndMetadata()
    {
        var container = new CompositionContainer(new TypeCatalog(typeof(Service)));

        Client c = new Client();
        container.SatisfyImportsOnce(c);

        Assert.AreEqual(1, c.Services.Count());
        Assert.IsInstanceOfType(c.Services.First().Value, typeof(Service));
        Assert.AreEqual("Hello", c.Services.First().Metadata.Meta);
    }
}

Dec 4, 2009 at 6:04 PM

Yes we designed specifically to support this. The one thing you may want to do is put an AttributeUsages attribute on your custom export indicating AllowMultiple=false. This way the metadata will be a single value rather than an array. For example see the export below.

[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
public class MessageSenderAttribute : ExportAttribute
{
    public MessageSenderAttribute() : base(typeof(IMessageSender)) { }
    public MessageTransport Transport { get; set; }
    public bool IsSecure { get; set; }
}

public enum MessageTransport
{
    Undefined,
    Smtp,
    PhoneNetwork,
    Other
}

Dec 10, 2009 at 3:45 PM

Thanks for fixing my code =) I should have started with the example.

Dec 10, 2009 at 4:25 PM

Is it possible to use Metadata with AllowMultiple=true? Or is it planned?

I suppose it would be right, to have several parts with different metadata instead of several parts and array metadata of every of them

Dec 10, 2009 at 9:09 PM

It is possible. The place where is most interesting is when you have loose metadata / custom metadata attributes (don't derive from export). For example imagine a [Category] metadata attribute that lets me specify multiple categories for an export.

 

Dec 11, 2009 at 6:48 PM

You can have as many different custom exports on a part as you need. Each one can appear once if you have AllowMultiple=False.

The problem with allowing a single attribute to appear multiple times (AllowMultiple=True) is that MEF, rather naturally, creates an IEnumerable-something (an array I think) to hold your multiple values.

When you look for the one value you are expecting, your import fails, and it can be quite difficult to track this problem down. AllowMultple=True is legal, I just would not use it except, as Glen pointed out, in cases where the metadata just screams "hey, there might be mroe than one of me" (like a category).

 

 

 

Dec 15, 2009 at 4:25 PM

ok, i've seen that mef creates that "IEnumerable-something (an array I think) to hold your multiple values." But I dont really understand why it does such weird thing, MetaAttribute derived from Export

[Meta(Bla=Foo)]

[Meta(Bla=bar)]

public void Some(object o)

--

After that there will be 2 parts in the catalog(and this is OK), but WHY do those parts have both metadata attributes data?