DCSIMG
MEF for Beginner (repeatable metadata) - part 9 - Bnaya Eshet

Bnaya Eshet

Disclaimer

MEF for Beginner (repeatable metadata) - part 9

MEF for Beginner (repeatable metadata) - part 9

this is the 9th post of the MEF for Beginner series, the series TOC is available here.

this post will focus on having repeatable metadata definition (cases like definition of multiple categories).

if you not familiar with the MEF metadata concept you may want to read part 8. 

MEF extensibility compose import export importmany

Bad practice for repeatable metadata

In order to explain repeatable metadata, we will start by decorating export with

untyped metadata declaration (which consider as bad practice, use typed metadata

whenever you can, it is mach usable and reducing runtime failure).

Code Snippet
  1. [Export(typeof(IPlug))]
  2. // using untyped metadata considered as bad practice
  3. [ExportMetadata("Categories", Category.Travel, IsMultiple = true)]  
  4. [ExportMetadata("Categories", Category.City, IsMultiple = true)]   
  5. public class NewYork : IPlug
  6. {
  7.     public void Write() { Console.WriteLine("New York"); }
  8. }

the above code snippet decorate the plug-in with categories  metadata for both Travel and City (line 3,4).

notice that we should explicitly declare that those decoration is multiple (IsMultiple=true).

line 1: define the export.

latter on this post we will see the best practice of declaring the metadata using custom attribute.

 

Metadata view contract for repeatable metadata definition

the metadata view contract properties type, for repeatable metadata decoration, should be Array, or IEnumerable.

the following metadata view contract is capable to handle the above metadata decoration:

Code Snippet
  1. public interface IMetadataView
  2. {
  3.     Category[] Categories { get; }
  4. }

as you can see, line 3, define property that match the name of our metadata decoration (Categories)

and it is using array for its data type.

 

Best practice for metadata definition

as in the previous part, the best practice for metadata definition is

using  typed metadata, which mean to define attribute for the metadata.

Code Snippet
  1. [MetadataAttribute]
  2. [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
  3. public class CategoryAttribute : Attribute
  4. {
  5.     public CategoryAttribute(Category category)
  6.     {
  7.         Categories = category;
  8.     }
  9.     public Category Categories { get; private set; }
  10. }

as you can see it is almost identical for the definition of non-repeatable metadata attribute,

but with 2 differences:

  • we should declare that the attribute allow multiple occurrence (at the end of line 2),
    this definition is equivalent to the IsMultiple definition on the untyped metadata.
  • we should derive from Attribute (instead of ExportAttribute, otherwise we will get multiple
    instantiation of our plug-in
    ).

 

the plug-in decoration will look as follow:

Code Snippet
  1. [Export(typeof(IPlug))]
  2. [Category(Category.Game)]
  3. [Category(Category.News)]
  4. public class Football : IPlug
  5. {
  6.     public void Write() { Console.WriteLine("Football"); }
  7. }

in term of code safety typed metadata leaving less margin for errors,

and it is match more usable because it is easier to understand which value are expected.

 

The Import decoration

our import should use the Lazy<IPlug, IMetadataView> in order to get the metadata information

(through the IMetadataView contract ).

Code Snippet
  1. [ImportMany]
  2. public Lazy<IPlug, IMetadataView>[] Plugins { get; private set; }

 

Interrogating the metadata

the following code will compose and interrogate the metadata.

Code Snippet
  1. class Program
  2. {
  3.     static void Main(string[] args)
  4.     {
  5.         var instance = new Program();
  6.         var cat = new AssemblyCatalog(typeof(Program).Assembly);
  7.         var container = new CompositionContainer(cat);
  8.         container.ComposeParts(instance);
  9.  
  10.         foreach (var plug in instance.Plugins)
  11.         {
  12.             plug.Value.Write();
  13.             foreach (var category in plug.Metadata.Categories)
  14.             {
  15.                 Console.Write(", {0}", category);
  16.             }
  17.         }
  18.     }
  19.  
  20.     [ImportMany]
  21.     public Lazy<IPlug, IMetadataView>[] Plugins { get; private set; }
  22. }

lines 6-8, is handling the composition.

line 10, iterating for each of the discovered plug-in.

line 13, iterating for each of the items in the metadata categories property upon specific plug-in.

 

Summary

having multiple metadata on the same export is slightly more challenging.

the main difference between working with repeatable metadata relate to the metadata decoration (AllowMultiple=true)

and having Array or IEnumerable at the metadata view properties level.

 

Download the code

the full code snippet for the samples covered by this post available in here,

full project download for the post sample and for WPF sample that using this post concept for

dynamic filtering of widgets by categories (as shown bellow), is available here for 2008, and here for 2010.

image 

filtering widgets by categories sample (2010, 2008).

 

Point of interest
  • do not use setter on your metadata view interface.
  • do not use the [InheritedExport] because for some some strange reason it doesn't functioning well along with metadata.
  • if you want to build advance metadata view contract you can use class instead of interface,
    but that class must have constructor with the following signature:
    public ClassName (IDictionary<string, object> metadata)
    inside the constructor you should map the metadata dictionary to the class properties.

 

 


kick it on DotNetKicks.com


Comments

No Comments