Monday, October 4, 2010

Using MEF to extend applications

I'm back for another post, haven't posted for a long time now and hope to never do so again.

Getting to the point here, today I started using MEF, after months of tech talks, webcasts and tutorials, and I have to admit, it's really easy, clean and effective.

My scenario is this:

I have an application that needs to have a caching system, which will be implemented on a different project, and needs to abstract itself from the caching system being use.
So I coded an interface, obviously, to define what does the caching system need to implement.

public interface IPersist
  {
    void SaveToCache(string key, string payload, List< guid > contentIds);

    string GetFromCache(string key);

    void RefreshCache(string contentId);
  }

Then I created two example caching classes that implement this interface, which both reside on different visual studio projects:

[Export(typeof(IPersist))]
public class MemoryCache : IPersist
{

  public void SaveToCache(string key, string payload, List< guid > contentIds)
  {
    Console.WriteLine("memory cache " + key + " " + payload + " " + contentIds.Count.ToString());
  }

  public string GetFromCache(string key)
  {
    throw new NotImplementedException();
  }

  public void RefreshCache(string contentId)
  {
    throw new NotImplementedException();
  }
}

So, we have an interface named IPersist, we have two classes that implement this interface, and now we need to dinamically load one of these classes on our application, so we cannot add a reference to neither of them, because that way we would have to change our code each time we changed class, so how do we do it?

Our application code looks like this:

public partial class App : Application
  {
    private IPersist cacheSystem { get; set; }

    public App()
    {

      //something is missing here, how to load the assembly we want?

      cacheSystem.SaveToCache("key", "payload", new List< guid >());

      cacheSystem.GetFromCache("key");
    }
  }

What do we have to change to use MEF?
Lets assume the assembly file of the class that implements our cache system will be on the executing directory of our application (it could be elsewhere, or the class could be on the same assembly as our application even, but on this example it will be on the executing directory).
First we have to specify that our classes "export" themselves:

[Export(typeof(IPersist))]
public class MemoryCache : IPersist
{

  public void SaveToCache(string key, string payload, List< guid > contentIds)
  {
    Console.WriteLine("memory cache " + key + " " + payload + " " + contentIds.Count.ToString());
  }

  public string GetFromCache(string key)
  {
    throw new NotImplementedException();
  }

  public void RefreshCache(string contentId)
  {
    throw new NotImplementedException();
  }
}

[Export(typeof(IPersist))]
public class DiskCache : IPersist
{

  public void SaveToCache(string key, string payload, List< guid > contentIds)
  {
    Console.WriteLine("disk cache " + key + " " + payload + " " + contentIds.Count.ToString());
  }

  public string GetFromCache(string key)
  {
    throw new NotImplementedException();
  }

  public void RefreshCache(string contentId)
  {
    throw new NotImplementedException();
  }
}

So we added an Export attribute, specifying which type we are exporting, and this is important when things get more complicated, when you are exporting and importing lots of types.

Then on our application, we must create a catalog, on this case a DirectoryCatalog, since we are getting our exported classes on a directory, and then we create a CompositionContainer and pass our catalog as a parameter. The CompositionContainer is responsible for browsing the provided catalog and resolve connect the dots (match the exported classes to the imported ones on our application), and the code we need to do this is:

public partial class App : Application
  {
    [Import]
    private IPersist cacheSystem { get; set; }

    public App()
    {

      var catalog = new DirectoryCatalog(Environment.CurrentDirectory);
      var container = new CompositionContainer(catalog);
      container.ComposeParts(this);

      cacheSystem.SaveToCache("key", "payload", new List< guid >());

      cacheSystem.GetFromCache("key");
    }
  }

Which is, create a property that imports something of the type IPersist, create the catalog for the current directory, create the container which will resolve the export/import stuff, and order this resolution with the ComposeParts(this) method call, and finally compile one of our IPersist classes, put it on the executing dir and run our application.
Bam! Magically all is tied up and we can switch cache systems without compiling, just switch the dll file and you can change, just like that, our caching system implementation.

So, how powerful is this?

Please give feedback.

Thank you all, take care.

No comments: