Why does RegistrationBuilder exclude parts decorated with a metadata attribute?

Jan 26, 2014 at 11:03 AM
As of introduction: I know that Export and ExportMetadata attributes must be both specified on a part, so that the metadata can be accessed. It's related to the issue of InheritedExport, where metadata couldn't be specified on a deriving class. I also I recall it was by design.

As I researched how to overcome this limitation by using convention-based model, the behaviour I observed appears inconsistent. Let me continue explaining here not to distract the reader, but please refer to the code sample at the end of this post.

In the code there's a convention, which says "export all parts implementing IExtension" (how it adds metadata is a temporary thing).

If there are any exceptions to the convention, it can be handled by decorating the part with attributes as it's done with the Ext2 class in the example. Then, that specific part is exported "directly" without being processed by the convention. Having both Export and ExportMetadata attributes on Ext2 means: "override the convention when dealing with part Ext2".

If Ext1 is not marked with any attributes it is processed by the RegistrationBuilder and giving me chance to handle it as desired. Until now the behavior is expected.

But now, uncomment the ExportMetadata attribute decorating Ext1. As mentioned at the beginning a part would need both Export and ExportMetadata attributes to be considered a complete composition part (as it's done with Ext2). That's fine yet.

What's unexpected here is that Ext1 is no more passed to the convention builder. It's neither exported directly or allowed to work with in the registration rule. It's simply inaccessible/undiscoverable.

It can be narrowed down to cases when you use only ExportMetadata or any other custom metadata attribute without the Export attribute itself. Remember that if Ext1 is decorated with any attribute that is not a MetadataAttribute, then Ext1 is accessible from registration builder.

I understand the reasoning behind the necessity of applying both Export/ExportMetadata. However, if the Export attribute is missing and a part is rejected as incomplete, I believe it should be passed to the registration builder as it is "a part not handled directly via attributes". Now, if and only if one decorates a part with a metadata attribute, it is rejected by the system, as if it didn't exist. It's handled neither by the attributed nor the attribute-free mechanisms in MEF. Notice that it is rejected even though the metadata is not requested anywhere explicitly (like it would be when lazily loading parts).

To my current understanding there's no reason, why such behavior would be desired. I hope someone from the team could take a closer look at the code below and share the opinion whether or not it's an issue to fix.
    class Program
    {
        static void Main(string[] args)
        {
            var conventions = new RegistrationBuilder();
            conventions.ForTypesDerivedFrom<IExtension>()
                .Export<IExtension>(b =>
                {
                    b.AddMetadata("meta", t => t); //this would be sth based on 't'
                });

            var conventionCatalog = new AssemblyCatalog(typeof(Program).Assembly, conventions);
            var container = new CompositionContainer(conventionCatalog);

            var shell = new Shell();
            container.ComposeParts(shell);
        }
    }

    class Shell
    {
        [ImportMany]
        public IEnumerable<IExtension> Extensions { get; set; }
    }

    interface IExtension
    {
    }

    //[ExportMetadata("meta", "overridden")]
    class Ext1 : IExtension { }

    [Export(typeof(IExtension)), ExportMetadata("meta", "fully attributed")]
    class Ext2 : IExtension { }
Aug 3, 2014 at 9:48 PM
Did you ever get an answer on this?