Plug-ins / Modules Development with MEF -Part II

INTRODUCTION

This is the continuation of my earlier post. I strongly encourage the readers to stop here and read the part one of this series if they haven’t read it yet. In part one we looked at the brief introduction on MEF and a practical scenario where this framework can be implemented. We talked about HRMS , I have code named it as MangoPeople :). This is not an HR solution but just peeps into it how MEF can help you to build these type of enterprise apps. So let us get jump started into it.

MEF APPLICATION

Our MangoPeople HR solution is built on WPF using Visual Studio 2010 RC in .Net Framework 4.The Visual Studio solution consists of 5 projects as shown in figure below.

solutionexplorer

  1. MangoPeopleHR : This is based on WPF Application Template. It is an empty shell and just have one Xaml Window. Look at the screen shot below which will give you the fair idea.
    MangoPeople Main Window
    On the Left hand side you will see the Modules that are available. If your client who has not purchased any of the modules , those links will not be active (You wont get an hand mouse pointer on that module link). On the Right hand side there is a content control which will hosts the main window or entry point screens of the modules selected. The output of this project is an .exe
  2. CommonStuff: This is project based on the standard windows class library project. It contains the helper class of handling commands for ViewModel and the other class has the MEF contract interface.
  3. PersonalModule:
  4. PayrollModule:
  5. RecruitmentModule:

The items 3,4 and 5 are based on WPF User Control Library Project Template whose output is the dll. I hope you must have now got the overall idea of the solution. Now the catch here is that all these modules should be pluggable. If you add them they become active and vice-versa. So to make them active you must copy the module dll into one of the designated folder, I call it MangoPeopleLic. In this folder if the module dll is present that module link will be active if removed it will be inactive. So time has come now to see the inside stuff.

MEF IMPLEMENTATION

Though these modules are independent of the main application. There should be some glue which should bind them once they are plugged in (dll is copied in the designated folder) and that is where the MEF contracts or interface comes in and play a vital role in overall MEF implementation. Let us look at the code

namespace CommonStuff
{
    public interface  IContract
    {
        string ModuleName { get; }
    }
}

The Icontract interface has only one read only property called ModuleName. Now any type that needs to expose these functionalities should be decorated with the Export attribute with typeof parameter. In our scenario all our UserControl main class or the entry points of every module should have this attribute. See the code below of one of PersonalMainView.xaml.cs

using System.Windows.Controls;
using CommonStuff;
using System.ComponentModel.Composition;

namespace PersonalModule
{
    /// <summary>
    /// Interaction logic for MainPersonalView.xaml
    /// </summary>
    ///
    [Export(typeof (IContract))]
    [ExportMetadata("ModuleName","Personal")]
    [ExportMetadata("ModuleId",1)]
    public partial class MainPersonalView : UserControl,IContract
    {
        public MainPersonalView()
        {
            InitializeComponent();
        }

        #region IContract Members

        public string ModuleName
        {
            get { return "Personal"; }
        }

        
    }
}

The above code besides inheriting the UserControl class also implement the IContract interface. It is also decorated with the Export and ExportMetaData attributes.All MangoPeopleHR modules user control classes have the same code except for the ExportMetaData attributes values and IContract interface property value. So we are done from the modules perspective and now let us jump to the MainWindowViewModel class which gets binded to the MainWindow. One of the most important property in this class is ModulesPurchased.

[ImportMany]
        public Lazy<IContract,IDictionary<string ,object>>[] ModulesPurchased  { get; set; }

This property is decorated with ImportMany attribute to load all the required composable parts also it is of Lazy type , which means the parts will be loaded on demand. Remember I talked about the glue earlier which binds the main exe with all the dll. This property is the glue here. In modules project the usercontrol classes if you remember we were using Export attribute of same contract and here we are using ImportMany attribute of the same contract. Does that make sense to you , Great !. Now let us look at the other methods which put MEF on fire.

private CompositionContainer GetLicenseInfo()
        {
            var objCatalog = new AggregateCatalog();
            var objAssembly = new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly());
            objCatalog.Catalogs.Add(objAssembly);
            objCatalog.Catalogs.Add (new DirectoryCatalog(LicenceDirectory ));
            var container=new CompositionContainer(objCatalog );
            return container ;
        }

The above code listing does the job of adding all the parts in the repository. We use AssemblyCatalog and DictionaryCatalog. The LicenceDirectory is the class variable which stores the path of the folder where all modules dll will be there. So now we have parts in the repository so now let us compose them , the code of which is described below.

public  bool CheckLicences()
       {
           _LicenceComposionContainer = GetLicenseInfo();

           try
           {
               _LicenceComposionContainer.ComposeParts(this);
           }
           catch (Exception)
           {

               System.Windows.MessageBox.Show("Unable to Retrieve the License Information");
               return false;
           }
           return true;
       }

After getting the value of the CompositionContainer from the GetLicenseInfo method. It tries to call the ComposeParts method.Returns a boolean value dependent on this method call. And now finally let us see the Command method which enables or disables the hyperlink based on the dlls available in the License directory.

private bool  CanModuleSelectExecute(object param)
        {
            bool isAllowed=false;
            foreach (var item in this.ModulesPurchased )
            {
                if (param.ToString().Equals(item.Metadata["ModuleId"].ToString()))
                {
                    isAllowed = true;
                    break;
                }
                else
                {
                    isAllowed =false ;
                }
            }
            return isAllowed;
        }

Here we rotate in the loop and compare the param value with the metadata in the array of ModulesPurchased property. If the match is found the function returns true which will enable the link else the link will be disabled. The param parameter of object type is binded in the Xaml via CommandParameter property.

CLOSURE

So you just saw how MEF simplifies the application partitioning.Though in past we had such frameworks which provided this type of functionalities like Composite Application Blocks,PRISM and Unity to name few. But MEF is different and with ease it can be integrated. It is now the official part of the CLR, what more do you accept to understand its importance.So get started now and start using MEF. You can download the sample code of this post from here. Please ensure to change the value of LicenseDirectory variable to suit to your enviornment otherwise you will get error. Do let me know your thoughts on MEF.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s