Sunday, January 20, 2013

Fun with the Microsoft Managed Extensibility Framework Part 2

In the last installment we introduced the basic concepts behind the Microsoft Managed Extensibility Framework and built a very simple example, expending a ton of effort for very little return.  In this installment, we’ll actually utilize the framework to build an application demonstrating pluggable components in use.
The biggest advantage of using the Managed Extensibility Framework is the ability to componentize your application.  Our original example only allowed you to load a single, specific part.  With that in mind, we will revisit our original example, and carefully refactor it to allow multiple parts
We left our payroll project in the following state:
 public class Employee {     public string Name { get; set;}     public decimal HourlyRate { get; set;} }public class PayrollManager{  [Import]   private PayrollProcessor _processor;  public PayrollManager()  {       // Grab our catalog and fill in our container       AggregateCatalog catalog = new AggregateCatalog();       CompositionContainer container = new CompositionContainer(catalog);          catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));       container.ComposeParts(this);   }}[Export]public class PayrollProcessor{   public decimal ComputePayrollAmount(Employee employee, decimal hours)   {       return employee.HourlyRate*hours;   }        }public void Main(){     List employees = new List                             {                              new Employee { HourlyRate = 90.0m, Name = "Bob"},                              new Employee { HourlyRate = 45.0m, Name = “Pete”},                              new Employee { HourlyRate = 70.0m, Name = “Wendy”}                             };           PayrollManager payrollManager = new PayrollManager();           foreach (Employee employee in employees)           {               decimal paycheck = payrollManager.CalculatePayroll(employee, 40);               Console.WriteLine("Name: {0} Total: {1}", employee.Name, paycheck);           }}Now, let’s assume that we’ve grown our business and we now process payrolls for multiple companies, according to different rules.  Some pay overtime, some do not for example.  We can of course hard code each company but that’s going to make adding new companies a pain.  Instead what we’ll do is create a payroll part for each company and call the appropriate one based on each employee’s associated company.
So first, we need to add a field to Employee to store the company name:
public class Employee
 {     public string Name { get; set;}     public decimal HourlyRate { get; set;}     public string Company { get; set; } }Now, we can assign each employee to a company and ensure the right payroll part is used.  As a first step to allow using different parts for each, we will rewrite our PayrollProcessor class by first extracting a common interface for each payroll processor.  Incidentally, this would have been a good thing to do earlier on to enable easier unit testing.
So now, our new interface will look like:
  public interface IPayrollProcessor   {       string CompanyName { get; }       decimal ComputePayrollAmount(Employee employee, decimal hours);   }We will use CompanyName to identify each processor when matching it with employees.
Our original processor will now be rewritten as follows, assuming its company name is widget.com.
   [Export(typeof(IPayrollProcessor))]    public class WidgetPayrollProcessor : IPayrollProcessor   {       #region Implementation of IPayrollRequest       public decimal ComputePayrollAmount(Employee employee, decimal hours)       {           return employee.HourlyRate*hours;       }       public string CompanyName       {           get           {               return "Widget.com";           }       }       #endregion   }A couple things to note here: We are implementing the IPayrollProcessor interface, and we are exporting the part.  Unlike the simpler example shown earlier, we need to identify this class in our export as implementing our interface.  This is because we will now be importing multiple parts and we need some way to say “give me all the payroll parts” instead of “give me the widget.com part, the Microsoft part, etc”
Now that we’ve rewritten our original part, we’ll implement another class part so we can show how to use them as pluggable components:
   [Export(typeof(IPayrollProcessor))]    public class MicrosoftPayrollProcessor : IPayrollProcessor   {       #region Implementation of IPayrollRequest       public decimal ComputePayrollAmount(Employee employee, decimal hours)       {         decimal totalPay = 0.0m;         if (hours > 40)           {              // pay time and ½ after 40 hours              totalPay =  employee.HourlyRate*(1.5*Hours-20);           }           else           {              totalPay =  employee.HourlyRate*;           }       }       public string CompanyName       {           get           {               return "Microsoft";           }       }       #endregion   }So now, we’ve got our basic program structure and a couple of parts built.  We need to tie them all together.  As before, we need to use a CompositionContainer to allow us to associate our exported parts with corresponding imports.  Now however, we will get a bit more complicated, as we have multiple exports.
First, we will once again use an AggregateCatalog to find our various parts and will reference the executing assembly.  In the next installment, we’ll show you how to break this up so we can import parts from multiple assemblies.
Probably the biggest change will be the introduction of the CompositeBatch class and the ImportMany attribute.  While CompositeBatch is not required, it allows us to add or remove multiple parts to the container as a single transactional action.   Using it guarantees we’ll get all our parts.
ImportMany is what allows us to grab multiple payroll processors.  Unlike Import, which is always one to one, ImportMany will cause all matching exports currently in the CompositeContainer to be associated with a collection of some sort.  All we need to do is declare a list of our interface type.  For example:
ImportMany(typeof(IPayrollProcessor))]       private List _companies = new List();So the heart of our app, the PayrollManager now looks like:   public class PayrollManager   {// Instead of just getting a single instance of class, we’ll now end up with// A list of objects which implement the IPayrollProcessor interface       [ImportMany(typeof(IPayrollProcessor))]       private List _companies = new List();       public PayrollManager()       {           // Get all components and stuff them in for lookup upon execution           AggregateCatalog catalog = new AggregateCatalog();           CompositionContainer container = new CompositionContainer(catalog);           CompositionBatch batch = new CompositionBatch();           batch.AddPart(this);           catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));           container.Compose(batch);       }       public List Processors       {           get            {               return _companies;           }        }       public decimal CalculatePayroll(Employee employee, decimal hours)       {           IPayrollProcessor payrollProcessor = Processors.Find(processor => processor.CompanyName.ToLower() == employee.Company.ToLower());           if (payrollProcessor!=null)           {               return payrollProcessor.ComputePayrollAmount(employee, hours);           }           return 0.0m;       }   }To give this code a test spin, here is a small test:public void EvaluatePayrollCalculations(){List employees = new List                                      {                                          new Employee                                                  {                                                      Company = "Widgets.com",                                                      EmployeeType = "Employee",                                                      HourlyRate = 90.0m,                                                      Name = "Larry"                                                  },                                              new Employee                                                  {                                                      Company = "Microsoft",                                                      EmployeeType = "Employee",                                                      HourlyRate = 45.0m,                                                      Name = "Adam"                                                  }                                          };           PayrollManager payrollManager = new PayrollManager();           foreach (Employee emp in employees)           {               decimal paycheck = payrollManager.CalculatePayroll(emp, 40);  Console.WriteLine("{0}-Hours:{1} Rate:{2} Total:{3}",                    emp.Name, 40, emp.HourlyRate, paycheck);           }       }Now, we have a pluggable solution.  To add another company, just define its payroll processor implementation and you are good to go.  Of course, right now, we require that they are all in the same assembly.  In the next installment, we’ll fix that
Using the Microsoft Managed Extensibility Framework we’ve easily modified our application to use a pluggable architecture.  MEF is very simple to use and doesn’t require very much in the way of configuration, just clean, interface based code.   You probably should code this way anyway if unit testing is something you do (and if you don’t, why not?).
Next installment, we’ll look at how to break these parts across assemblies and tackle some additional features.
Written by Larry Schoeneman.

Larry Schoeneman is a software developer with over 20 years of experience, focused on Microsoft products and technologies as well as utilizing agile methodologies to build better software.
If you found this post useful you may also want to check these out:
Fun with the Microsoft Managed Extensibility Framework Part 1Zend Framework: The Best Framework for Use With Other FrameworksChoosing a frameworkThe Zend Framework, Dependency Injection and Zend_DiZend Framework TutorialManaged Hosting – What’s it all about?
View the original article here

No comments:

Post a Comment