27 April 2010

A very basic MEF sample using ImportMany

If you have been programming since the mid-80’s and have been a IT professional for over 17 years it does not happen often anymore that you get mesmerized by a beautiful piece of architecture, but recently it happened twice to me in a very short time, and one of the causes was the Managed Extensibility Framework or MEF. Almost everyone has built some or other extensible architecture at one point but never before I have seen something as simple, elegant and universally applicable as this.

This article describes the setup of a very basic MEF driven application, using VS2010 Pro and .NET 4. It’s kind of abstract: a hosting component that accepts two strings, and calls one or more MEF components to actually do the manipulation. Every component has its own library, which may seem a bit overkill, but I wanted to check out the extensibility to the max. So I started out with an empty solution and then used the following track

1. Create a class library "Contracts"
This will contain the interface IMyComponent by which the components communicate:
namespace Contracts
{
  public interface IMyComponent
  {
    string Description { get; }
    string ManipulateString(string a, string b);
  }
}

as you can see, one hell of a complex component we are making here ;-)

2. Create a class library "ImportingLib"
This will contain the host, i.e. the class that actually hosts and calls the components. Add a reference to the "Contracts" projects as well as to "System.ComponentModel.Composition". Then Add a class "Importer", with the following code:
using System;
using System.Collections.Generic;
using System.Linq;
using Contracts;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;
using System.IO;

namespace ImportingLib
{
  public class Importer
  {
    [ImportMany(typeof(IMyComponent))]
    private IEnumerable<IMyComponent> operations;

    public void DoImport()
    {
      //An aggregate catalog that combines multiple catalogs
      var catalog = new AggregateCatalog();
      //Adds all the parts found in all assemblies in 
      //the same directory as the executing program
      catalog.Catalogs.Add(
       new DirectoryCatalog(
        Path.GetDirectoryName(
         Assembly.GetExecutingAssembly().Location)));

      //Create the CompositionContainer with the parts in the catalog
      CompositionContainer container = new CompositionContainer(catalog);

      //Fill the imports of this object
      container.ComposeParts(this);
    }

    public int AvailableNumberOfOperations
    {
      get
      {
        return (operations != null ? operations.Count() : 0);
      }
    }

    public List<string> CallAllComponents( string a, string b)
    {
      var result = new List<string>();
      foreach( var op in operations )
      {
        Console.WriteLine(op.Description);
        result.Add( op.ManipulateString(a,b ));
      }
      return result;
    }
  }
}

This deserves some explanation. This host imports 0 or more (ImportMany) components implementing IMyCompoment - and wants them delivered into the private property operations, thank you. The method "DoImport" can be called to initialize this object - Path.GetDirectoryName( Assembly.GetExecutingAssembly().Location) gives the directory in which the executing assembly (i.e. the program or the test) project resides, and by creating a DirectoryCatalog on that directory and then adding that to the main AggregateCatalog you make MEF automatically start searching all the assemblies in the directory where the calling program resides. Property AvailableNumberOfOperations of the Importer returns the number of found operations, and CallAllComponents calls all the IMyComponent exporting components and returns the result in one go.

To prove this actually works, we continue:

3. Create a class library "ExportingLib1"
Add a reference to "Contracts" and "System.ComponentModel.Composition", then add a class "TestComponent1" with the following code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Contracts;
using System.ComponentModel.Composition;

namespace ExportingLib1
{
  [Export(typeof(IMyComponent))]
  public class TestComponent1 : IMyComponent
  {
    #region IMyComponent Members
    public string Description
    {
      get { return "Concatenates a and b"; }
    }

    public string ManipulateString(string a, string b)
    {
      return string.Concat(a, b);
    }
    #endregion
  }
}

as you can see, this utterly exiting class exports IMyComponent, delivers a description of itself and concatenates the two strings to 1

4. Create a class library "ExportingLib2"
Add a reference to "Contracts" and "System.ComponentModel.Composition", then add a class "TestComponent2" with the following code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Contracts;
using System.ComponentModel.Composition;

namespace ExportingLib2
{
  [Export(typeof(IMyComponent))]
  public class TestComponent2 : IMyComponent
  {
    #region IMyComponent Members
    public string Description
    {
      get { return "Removes b from a"; }
    }

    public string ManipulateString(string a, string b)
    {      
      return a.Replace(b, string.Empty);
    }
    #endregion
  }
}
Again, a very complex class ;-), this time it removes all occurrences from b in a.

5. Create a test project "ImportingLib.Test"

Add references to ImportingLib, ExportingLib1 and ImportingLib2 and add the following test methods:

[Test]
public void TestCountComponents()
{
  var t = new Importer();
  t.DoImport();
  Assert.AreEqual(2, t.AvailableNumberOfOperations);
}

[Test]
public void TestOperations()
{
  var t = new Importer();
  t.DoImport();
  var result = t.CallAllComponents("all are equal ", "all");
  Assert.IsTrue( result.Contains( "all are equal all"));
  Assert.IsTrue( result.Contains( " are equal "));
}
If you followed my ‘recipe’ correctly, both tests will succeed. In addition, since you have added a Console.WriteLine in the CallAllComponents method, on the standard console output you will see "Concatenates a and b" and "Removes b from a", indicating the components that have been called. As you see, nowhere in code the components are actually instantiated - this is all done by MEF. The fun thing is, just by removing the reference to either ExportingLib1 or ExportingLib2 you can make the test fail. Nowhere are any explicit links between host and components but in the actual calling program itself, and those links are only made by the very presence of an assembly in the directory of the current executing program, or in this case, the test project.

So, you can dump more assemblies exporting IMyComponent - and the host will automatically pick them up. #Thatwaseasy

Parts of this sample are based on this description

Update 06-07-2012: I've added a full demo solution to this post to show how things are put together

01 April 2010

Caveats when migrating existing CLSA.NET objects to Silverlight

So you have been working for a while with CSLA .NET by Rockford Lhotka and now you want – like me – jump on the Silverlight bandwagon. So you want to reuse the business objects in Silverlight. There are some nice samples in the cslalight samples but when I started to try to use my own business objects, things did not run so smoothly as the samples suggest.

The samples by Rockford show the general outline:

  • You make a second assembly into which you add the exisiting business class source files as a link
  • You start adding specific Silverlight functionality surrounded by
    #if SILVERLIGHT (…) #endif preprocessor directives
  • You make sure that the server stuff like data access is not active in the Silverlight configuration (#if !SILVERLIGHT)
  • You add a Silverlight-specific factory method to load an object, that looks a bit like this:
public static void Get(int id, 
  EventHandler<DataPortalResult<MyBusinessClass>> callback)
{
  var dp = new DataPortal<MyBusinessClass>( );
  dp.FetchCompleted += callback;
  dp.BeginFetch( new IdCriteria(id) );
}

If you try to call this from your Silverlight client the result is - unless you are very lucky - most likely that the sky starts caving in. Turns out there are a few 'hidden requirements' –or at least some less apparent ones. Maybe there are more, but this was what I found so far:

  • Both the Silverlight and the full framework assemblies must have the same name, so even if your projects are called MyLib.Server and MyLib.Client, the resulting dll’s must have the same name, for example MyLib.dll.
  • If your full framework assembly is signed, your Silverlight assembly should be signed as well. They then also must have the same version number – all the way to the build number. This is important – and it took me the most time before the penny dropped.
  • All the Silverlight classes must have public constructors. So you add
#if! SILVERLIGHT   
  private MyBusinessClass()
  {
  }
#else
  public MyBusinessClass()
  {
  }
#endif
  • Properties should be defined in the ‘modern’ format. You have still properties in this format?
private string _oldProp = string.Empty;

public string OldProp
{
  get
  {
    return _oldProp;
  }	 
  set
  {
    if (value == null) value = string.Empty;
    if (!_oldProp.Equals(value))
    {
      _oldProp = value;
      PropertyHasChanged("OldProp");
    }
  }
}
Tough luck. Change that into the 'new' form, e.g.
private static PropertyInfo NewPropProperty = 
  RegisterProperty(c => c.NewProp);
public string NewProp
{
	get { return GetProperty(NewPropProperty); }
	set { SetProperty(NewPropProperty, value); }
}
  • Criteria objects should have public constructors as well, and should be public classes – that is, if you have defined them as private classes inside your business object, you should make them public
  • Your Criteria should implement IMobileObject. The easiest way is to let your class descend from CriteriaBase, but then you will find out that although the class is serialized to the server, the properties are not. Turns out that for Criteria objects the property format has changed too. In the past you could just make a simple class with a few getters and setters, now you have to make something along this line:
[Serializable]
public class IdCriteria : CriteriaBase
{
  public static PropertyInfo<int> IdProperty = 
    RegisterProperty(typeof(IdCriteria), new PropertyInfo<int>("Id"));
  public int Id
  {
    get { return ReadProperty(IdProperty); }
    set { LoadProperty(IdProperty, value); }
  }
  public IdCriteria() { }

  public IdCriteria(int id)
  {
    Id = id;
  }
}

So, although CLSA ‘light’ promises a lot of reuse (which is true of course, in the case of business and validation rules) you need a lot of extra plumbing to get going. And mind you, this is a simple single object that I only read – I haven’t covered lists yet, nor updates and deletes. The power of CSLA can come to Silverlight – but certainly for existing libraries it is not quite a free ride. But then again - this is Silverlight, so it should run on Windows Phone 7 series as wel... which will be my next experiment. I will keep you posted!