Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Advanced MEF Programming

3.80/5 (5 votes)
6 Feb 2011CPOL4 min read 34.5K  
Advanced MEF Programming

Types of Imports

.NET 4.0 introduced many new programming concepts and MEF supports some of them. MEF supports several import types, including dynamic, lazy, prerequisite, and optional.

The attached source code contains some of the advanced MEF programming concepts that I did not discuss in this blog. You can debug the source code step by step and explore the concepts.

Dynamic Imports

.NET 4.0 introduces a new type, dynamic. The type is a static type, but an object of type dynamic bypasses static type checking. In most cases, it functions like it has type object. At compile time, an element that is typed as dynamic is assumed to support any operation. Therefore, you do not have to be concerned about whether the object gets its value from a COM API, from a dynamic language such as IronPython, from the HTML Document Object Model (DOM), from reflection, or from somewhere else in the program. However, if the code is not valid, errors are caught at run time.

In some cases, the importing class may want to match exports of any type that have a particular contract name. In this scenario, the class can declare a dynamic import. The following import matches any export with contract name "DemoOfDynamicImport".

C#
1  [Import("DemoOfDynamicImport")]
2  public dynamic somedynamicclassobject;

When the contract type is inferred from the dynamic keyword, it will match any contract type. In this case, an import should always specify a contract name to match on. (If no contract name is specified, the import will be considered to match no exports.) Both of the following exports would match the previous import. The importing class must be prepared to deal with an object of arbitrary type.

C#
1  [Export("DemoOfDynamicImport", typeof(IGreeting))]
2  public class DemoOfDynamicImportClass
3  {
4      public string SayHelloWorld()
5      {
6          return "Hello from DemoOfDynamicImport Class";
7      }
8  }
Lazy Imports

In some cases, the importing class may require an indirect reference to the imported object, so that the object is not instantiated immediately. In this scenario, the class can declare a lazy import by using a contract type of Lazy<T>. The following importing property declares a lazy import. To explain multiple concepts, I created different objects using different techniques. Please use any one at your convenience.

C#
1  [Import("IMyLazyClass",AllowDefault = true)]
2  public Lazy<MyLazyClass> objlazyclass;
3
4  [ImportMany]
5  public Lazy<IGreeting>[] Greetings1 { get; set; }
6
7  [ImportMany]
8  public IEnumerable<Lazy<IGreeting>> Greetings2 { get; set; }

From the point of view of the composition engine, a contract type of Lazy<T> is considered identical to contract type of T. Therefore, the previous import would match the following export. The following Export matches the Import in line1 of the above code.

C#
1  [Export("IMyLazyClass")]
2  class MyLazyClass
3  {
4      public string SayHelloWorld()
5      {
6          return "Hello from MyLazyClass";
7      }
8  }

The following Export code matches the Import of line4 or line7 of the above Import code. The contract name and contract type can be specified in the Import attribute for a lazy import:

C#
 1  [Export(typeof(IGreeting))]
 2  public class SimpleGreetingClass : IGreeting
 3  {
 4      public string SayHelloWorld()
 5      {
 6          return "Hello World !!!";
 7      }
 8
 9      //Explicitly specifying a generic type.
10      [Export(typeof(Func<int, string>))]
11      public string DoSomething(int TheParam)
12      {
13          return "From Func: You Entered " + TheParam.ToString();
14      }
15  }

Optional Imports

The Import attribute specifies a requirement for the part to function. If an import cannot be fulfilled, the composition of that part will fail and the part will not be available. You can specify that an import is optional by using the AllowDefault property. In this case, the composition will succeed even if the import does not match any available exports, and the importing property will be set to the default for its property type (null for object properties, false for Booleans, or zero for numeric properties.) The following class uses an optional import.

C#
1  [Import("IMyLazyClass", AllowDefault = true)]
2  public Lazy<MyLazyClass> objlazyclass;

Metadata

Exports can provide additional information about themselves known as metadata. Metadata can be used to convey properties of the exported object to the importing part. The importing part can use this data to decide which exports to use, or to gather information about an export without having to construct it. For this reason, an import must be lazy to use metadata. To use metadata, you typically declare an interface known as a metadata view, which declares what metadata will be available. The metadata view interface must have only properties, and those properties must have get accessors. To allow the accessing of metadata, MEF leverages a new API of the .NET Framework 4, System.Lazy<T>. It allows delaying the instantiation of an instance until the value property of the Lazy is accessed. MEF further extends Lazy<T> with Lazy<T,TMetadata> to allow accessing export metadata without instantiating the underlying export. TMetadata is a metadata view type. A metadata view is an interface that defines read-only properties that correspond to keys in the exported metadata. When the metadata property is accessed, MEF will dynamically implement TMetadata and will set the values based on the provided metadata from the export.

The following class is an example metadata. See line25.

C#
 1  using System;
 2  using System.ComponentModel.Composition;
 3  using ContractsLibrary;
 4
 5  namespace MEFPart1
 6  {
 7      [Export(typeof(IGreeting))]
 8      public class SimpleGreetingClass : IGreeting
 9      {
10          public string SayHelloWorld()
11          {
12              return "Hello World !!!";
13          }
14
15          //Explicitly specifying a generic type.
16          [Export(typeof(Func<int, string>))]
17          public string DoSomething(int TheParam)
18          {
19              return "From Func: You Entered " + TheParam.ToString();
20          }
21      }
22
23
24      [Export(typeof(IGreeting))]
25      [ExportMetadata("Name", "Kishore")]
26      public class SimpleMetaDataClass : IGreeting
27      {
28          public string SayHelloWorld()
29          {
30              return "Hello World from SimpleMetaDataClass.
       Checking the metadata name at runtime !!!";
31          }
32      }
33  }

The following class is an example metadata. See line11.

C#
 1  using System;
 2  using System.Collections.Generic;
 3  using System.Linq;
 4  using System.Text;
 5  using System.ComponentModel.Composition;
 6  using ContractsLibrary;
 7
 8  namespace MEFPart2
 9  {
10      [Export(typeof(IGreeting))]
11      [ExportMetadata("Name", "Babu")]
12      public class ExtendedGreeting : IGreeting
13      {
14          public string SayHelloWorld()
15          {
16              return "Hello " + Environment.UserDomainName + "\\" +
                       Environment.UserName;
17          }
18      }
19
20      //[PartNotDiscoverable]
21      // A part decorated with PartNotDiscoverabl attribute
        // will not be included in any catalogs.
22      //So in the main exe, you will get runtime composition error
23      [Export("DemoOfDynamicImport", typeof(IGreeting))]
24      public class DemoOfDynamicImportClass
25      {
26          public string SayHelloWorld()
27          {
28              return "Hello from DemoOfDynamicImport Class";
29          }
30      }
31  }

An import with metadata is declared as a lazy import, with the metadata interface as the second type parameter to Lazy<T,T>. The following class imports the previous part with metadata.

C#
1  [ImportMany]
2  public IEnumerable<Lazy<IGreeting, IGreetingMetadata>> greetingmetadata

Once a collection of lazy exports with metadata has been imported, you can use LINQ (commented) to filter against the set as shown in the following code snippet:

C#
 1  foreach (Lazy<IGreeting, IGreetingMetadata>
       greetingmeta in greetingmetadataobjects)
 2  {
 3      if (greetingmeta.Metadata.Name == "Kishore")
 4      {
 5          Console.WriteLine(greetingmeta.Value.SayHelloWorld());
 6          //or we can create an object depending on the metadata value at runtime
 7          IGreeting objgreeting = greetingmeta.Value;
 8          Console.WriteLine(objgreeting.SayHelloWorld());
 9      }
10  }
11
12  ////The below commented code also performs the same task as the following code.
13  ////Following is the code incase, you want to create an object of a class using LINQ
14  //IEnumerable<IGreeting> objgreeting = greetingmetadataobjects.Where
       (v => v.Metadata.Name.Equals("Kishore")).Select(v => v.Value);
15  //foreach (var greeting in objgreeting)
16  //{
17  //    if (greeting != null)
18  //    {
19  //        Console.WriteLine(greeting.SayHelloWorld());
20  //    }
21  //}
Source Code is available at

Microsoft: http://cid-38ecce05b21b8b44.office.live.com/self.aspx/MY%20Projects/All%20MEF%20Concepts.zip

"Do not follow where the path may lead. Go instead where there is no path and leave a trail."


Filed under: Advance MEF Concepts, CodeProject, MEF

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)