IsMultiple for strongly typed metadata

Feb 26, 2009 at 8:47 AM
I like the way multiple metadata can be organized by means of ExportMetadataAttribute. 
However I wonder if there any possibility declaring the same with custom attributes?
Feb 27, 2009 at 2:28 AM
Edited Feb 27, 2009 at 2:28 AM
We automatically do the right thing if you mark the attribute as AllowMultiple=true.

Here's an example, given the following:

    public interface IDocumentFactoryMetadata
    {
        IEnumerable<string> ContentType
        {
            get;
        }
    }

    public interface IDocumentFactory
    {
    }

    [Export(typeof(IDocumentFactory))]
    [ContentType("*.txt")]
    [ContentType("*.log")]
    public class TextDocumentFactory : IDocumentFactory
    {

    }

    [MetadataAttribute]
    [AttributeUsage(AttributeTargets.Class, AllowMultiple=true)]
    public class ContentTypeAttribute : Attribute
    {
        private readonly string _contentType;

        public ContentTypeAttribute(string contentType)
        {
            _contentType = contentType;
        }

        public string ContentType
        {
            get { return _contentType; }
        }
    }

The following code outputs both *.txt and *.log:

            TypeCatalog catalog = new TypeCatalog(typeof(TextDocumentFactory));

            CompositionContainer container = new CompositionContainer(catalog);

            foreach (var export in container.GetExports<IDocumentFactory, IDocumentFactoryMetadata>())
            {
                foreach (string contentType in export.MetadataView.ContentType)
                {
                    Console.WriteLine(contentType);
                }
            }

Regards

David
Feb 27, 2009 at 6:56 AM
David,
I've already found out that yesterday, great stuff :)
Also I've played with multiple "multiples" at a time and it seems to work wonderfull.
I've tried something like the following:

public interface ISomeDocumentMetadata
{
  IList<string> ContentType { get; }
  IList<string> Extension { get; }
}

Next I've supported standard custom attribute with "AllowMultiple=true" to fill one row at a time, something like
[SomeAttribute("contentType1", "extension1")]
[SomeAttribute("contentType2", "extension2")]

This worked like a magic, however I suspect I have found a bug in MEF

When you start combining custom attributes with native MEF approach things get wrong, for example the following combination:

[ExportMetadata("ContentType", "contentType0")]
[ExportMetadata("Extension", "extension0")]
[SomeAttribute("contentType1", "extension1")]
[SomeAttribute("contentType2", "extension2")]

will break sorting at runtime, my custom attributes will get the wrong metadata (from wrong places), haven't tried two different custom attributes at once though

But that's a specific situation as far as I can understand.
The overall behavior is awesome!
Thanks
Developer
Mar 1, 2009 at 12:38 AM
I wouldn't even expect the following to work:
[ExportMetadata("ContentType", "contentType0")]
[ExportMetadata("Extension", "extension0")]
[SomeAttribute("contentType1", "extension1")]
[SomeAttribute("contentType2", "extension2")]

You would need to add IsMultple=true like such:
[ExportMetadata("ContentType", "contentType0", IsMultplie=true)]
[ExportMetadata("Extension", "extension0", IsMultplie=true)]
[SomeAttribute("contentType1", "extension1")]
[SomeAttribute("contentType2", "extension2")]

However, as far as sorting... there is no guarantee what order these are going to be in. We simply call GetCustomAttributes and walk the list and I don't know if the compiler makes any guarantee about the order these are stored in the assembly metadata. So I wouldn't depend on the sorting order. If you need the two list to line-up exactly then I would stick with the custom attribute approach.