Tuesday, June 23, 2009

Managed Extensibility Framework - Part2

This is in continuation with my previous post, Part1 of this series. Part1 covered the problem MEF tried to solve. I will start this part directly with example and then I will explain various sections in detail.

As of now MEF is all in one dll named System.ComponentModel.Composition. You can download this framework from codeplex.

It is currently in Preview 5. OK to get a quick grasp of this framework let us create three solutions.

  1. Console application which can consume extensible parts
  2. Class library which contains contract, rather interfaces
  3. Class library which contains one of the extensible parts that implements contract

Console application which can consume extensible parts

  1. Create a console application and call it “meflab1”
  2. Add reference to System.ComponentModel.Composition.dll (from bin directory of downloaded zip file)
  3. Replace code in Program.cs with following snippet
    using System;
    using System.IO;
    using System.Reflection;
    using System.ComponentModel.Composition;
    using System.ComponentModel.Composition.Hosting;
    using System.ComponentModel.Composition.Primitives;
    namespace meflab1
    {
    class Program
    {
    [Import]
    public IGreetings Greetings { get; set; }

    static void Main(string[] args)
    {
    Console.WriteLine("Enter 0 to quit, any other number to continue");
    while (Console.ReadLine() != "0")
    {

    Program program = new Program();
    program.Compose();
    program.Run();
    }
    }

    void Compose()
    {
    try
    {
    DirectoryCatalog catalog =
    new DirectoryCatalog(AppDomain.CurrentDomain.BaseDirectory);

    AggregateCatalog agcatalogue = new AggregateCatalog(new ComposablePartCatalog[] {catalog,
    new AssemblyCatalog(Assembly.GetExecutingAssembly())});

    CompositionContainer container = new CompositionContainer(agcatalogue);

    CompositionBatch batch = new CompositionBatch();

    batch.AddPart(this);

    container.Compose(batch);
    }
    catch (FileNotFoundException fnfex)
    {
    Console.WriteLine(fnfex.Message);
    }
    catch (CompositionException cex)
    {
    Console.WriteLine(cex.Message);
    }
    }

    void Run()
    {
    if (Greetings != null)
    {
    Console.WriteLine(Greetings.SayHello());
    }
    Console.Read();
    }
    }
    }

  4. Add a class by name SimpleGreeting and replace code in SimpleGreeting.cs with following snippet
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.Composition;

    namespace meflab1
    {
    [Export(typeof(IContextInfo))]
    public class UserInfo : IContextInfo
    {
    public IDictionary<string, string> GetContextInfo()
    {
    return new Dictionary<string, string> { { "UserName",
    Environment.UserDomainName + "\\" + Environment.UserName } };
    }
    }
    }
  5. Save this solution.

Class library which contains contract, rather interfaces


  1. Create a class library and call it “meflibrary”
  2. Rename Class1.cs to Contract.cs
  3. Now replace contents of Contract.cs with following snippet. We are just defining two interfaces here
    using System.Collections.Generic;
    namespace meflab1
    {
    public interface IContextInfo
    {
    IDictionary<string, string> GetContextInfo();
    }

    public interface IGreetings
    {
    string SayHello();
    }
    }
  4. Build it and add reference of this class library to application we built in last step

Class library which contains one of the extensible parts that implements contract

  1. Create a class library and call it as “mefpart”
  2. Add reference to System.ComponentModel.Composition.dll (from bin directory of downloaded zip file)
  3. Rename Class1.cs to SimpleGreeting.cs
  4. Now replace contents of SimpleGreeting.cs with following snippet. Here we are implementing one of the extensible part
    using System.ComponentModel.Composition;

    namespace meflab1
    {
    [Export(typeof(IGreetings))]
    public class SimpleGreeting : IGreetings
    {
    [Import(typeof(IContextInfo))]
    public IContextInfo ContextInfo { get; set; }

    public string SayHello()
    {
    string userName;
    var props = ContextInfo.GetContextInfo();
    props.TryGetValue("UserName", out userName);
    return "Hello " + (userName ?? "<null>") + " from Visual Studio 2010";
    }
    }
    }
  5. Build it

Having this infrastructure let us do two exercises

  1. Run application without dependent assembly
  2. Run application without dependent assembly during start up but make it available before JIT compiling

Run application without dependent assembly

Now build first solution and try to run it.

  1. Open command prompt
  2. Navigate to output folder of first solution
  3. start mefpart.exe. Application will be waiting at “Enter 0 to quit, any other number to continue”
  4. Enter any non-zero value. We end up with following error
The composition produced a single composition error. The root cause is provided below.
Review the CompositionException.Errors property for more detailed inf
ormation.

1) No exports were found that match the constraint
'((exportDefinition.ContractName = "meflab1.IGreetings") && (exportDefinition.Metadata.ContainsKey("Expor
tTypeIdentity") && "meflab1.IGreetings".Equals(exportDefinition.Metadata.get_Item
("ExportTypeIdentity"))))'.

Resulting in: Cannot set import 'meflab1.Program.Greetings
(ContractName="meflab1.IGreetings")' on part 'meflab1.Program'.
Element: meflab1.Program.Greetings (ContractName="meflab1.IGreetings")
--> meflab1.Program

This is expected as we don’t have any library implementing IGreetings interface.

Run application without dependent assembly during start up but make it available before JIT compiling

  1. Open command prompt
  2. Navigate to output folder of first solution
  3. start mefpart.exe. Application will be waiting at “Enter 0 to quit, any other number to continue”
  4. Copy mefpart.dll from output directory of mefpart’s solution to output directory of meflab1. That is nothing but current directory of command prompt.
  5. Enter any non zero value
  6. Observe that application could seamlessly load part and execute code in it

So what didn’t work in part-1 works now. I will leave explanation of various sections of this code to next post, i.e. Part-3.

No comments: