MEF Runtime Plugin Swapping

Introduction

Managed Extensibility Framework (MEF) is a solution by Microsoft to build applications from reusable components which can be discovered, reused and composed. It is best suited for applications which need plug-in kind of support. 

Problem

MEF provides attributed programming model. Using the predefined attributes on desired components MEF makes plug-in handling very easy. While most of the things are handled automatically, some situation may arise where one need to take extra steps. One such situation is “On the Fly” modification/deletion of existing plug-ins in an application. In this blog, we will be focusing on this particular requirement in MEF projects –

“Swapping out MEF enabled DLL on the fly”

Solution

The solution to the problem is “Shadow copying MEF enabled dlls”. To describe the solution, Lets go through a sample application. To understand the application, I assume the reader has basic understanding of MEF core concepts like – composable parts, imports, exports, contracts and composition etc.

Although for a quick refrence one  can follow below definitions-

  • Composable Part – A part provides services to other parts or may consume services of other parts
  • Export – An export is a service that a part provides
  • Import –An import is a service that a part  consumes
  • Contract – An identifier for import or export
  • Composition – MEF uses composition to instantiate parts and match exports with imports.

 The sample application is a Currency Converter application.  The application is using two converters (plug-ins).

One is providing service for “Conversion from Dollar to Rupee” and other “Pound to Rupee”. Both these plug-ins are marked with Export attributes.  See below –   

[Export(typeof(IConvert))]
[ExportMetadata(“ConverterMetaData”, “DollarToRupee”)]
public class DollarToRupee : IConvert
{
#region IConvert Members
public double ConvertCurrency(double value)
{
return value * 61.94; // Dummy Values
}
#endregion
}
[Export(typeof(IConvert))]
[ExportMetadata(“ConverterMetaData”, “PoundToRupee”)]
public class PoundToRupee : IConvert
{
#region IConvert Members
public double ConvertCurrency(double value)
{
return value * 100.56; // Dummy Values
}
#endregion
}
 

Both of the converters are placed in separate libraries and the final dlls of the two are put inside “D:/DLL/”  for test purpose.

The ConverterComposition class uses import, catalog and container  to get their parts composition done-

[ImportMany(typeof(IGatherer), AllowRecomposition=true)]
public System.Lazy<IGatherer>[] Operations { get; set; }

// AssembleConversionComponents method –
var aggCatalog = new AggregateCatalog();
var directoryPath = @”D:/DLL/”;
var dirCatalog = new DirectoryCatalog(directoryPath, “*.dll”);
aggCatalog.Catalogs.Add(dirCatalog);
var comContainer = new CompositionContainer(aggCatalog);
comContainer.ComposeParts(this);
 

To get the conversion service from the components the main app places some checks –

// ConvertCurrency method –
foreach (var ConvPlugin in CoversionPlugins)
{
     if ((string)ConvPlugin.Metadata[“ConverterMetaData”] == conversionType)
     {
          result = ConvPlugin.Value.ConvertCurrency(value);
          break;
     }
}
 

The things work fine, but the problem arises when the main application is running and one/two of the service providers want to update/delete their services/plugins. If you try to delete any of the dll from “D:/DLL”  folder, it will not let it done without stopping the main application. The reason behind is the current AppDomain will make a hold on the dlls and will not let them deleted.

To overcome this situation we can use ShadowCopy feature provided by AppDomainSetup class. Configure the proper settings for this class and pass these settings to new AppDomain object –

 

var cachePath = “D:/Shadow DLL/”;
var pluginPath = “D:/DLL”;
if (!Directory.Exists(cachePath))
{
    Directory.CreateDirectory(cachePath);
}
if (!Directory.Exists(pluginPath))
{
    Directory.CreateDirectory(pluginPath);
}
var setup = new AppDomainSetup
{
    CachePath = cachePath,
    ShadowCopyFiles = “true”,
    ShadowCopyDirectories = pluginPath
};
AppDomain domain = AppDomain.CreateDomain(“DynamicHost_AppDomain”, AppDomain.CurrentDomain.Evidence, setup);
ConverterComposition converter = (ConverterComposition)domain.CreateInstanceAndUnwrap(typeof(ConverterComposition).Assembly.FullName, typeof(ConverterComposition).FullName);
converter.AssembleConversionComponents();
 

Call the desired converter –

var result = converter.ConvertCurrency(Convert.ToDouble(txtCurrency.Text), “PoundToRupee”);
 

If you now go to DLL folder and try to delete any of the plug-in, you will be able to do so while main application is still running.  The reason behind this is that the main application is now using Shadow copy of the plug-in dlls, which are placed inside D:/Shadow DLL/ folder path. 

The sample application CurrencyConverter is attached with this blog (The DollarToRupee converter is in same project so you need to extract the dll from there and need to put it in D/DLL location).

2 thoughts on “MEF Runtime Plugin Swapping

  1. Dear Sumit,
    i had tryed using your suggetion but i am getting Type ‘ConverterComposition.ConverterCompost’ in assembly ‘ConverterComposition, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null’ is not marked as serializable

    i marked [Serializable] to ConverterCompost class then its goes on looping infinety.

    regards

    • I know that your question is too old, but can help others.
      You need to inherit from MarshalByRefObject and not mark as Serializable.

Leave a Reply

Your email address will not be published. Required fields are marked *


three × = 18

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>