Introduction
Most developers at one time or another have found it necessary to componentize their application to allow new pieces to be “plugged in” without rebuilding the application. While there are existing frameworks which address this area, such as one of the many Dependency Injection frameworks, they tend to be much broader in scope, and therefore more complicated to use. Microsoft has built the managed extensibility framework (MEF) with simplicity in mind. It is easy to use and can quickly allow you to componentize your app.
Getting Started
MEF is officially part of Visual Studio 2010. If you are using Visual Studio 2008, the codeplex site will hopefully have a version you can use. Its location is at http://mef.codeplex.com. Still, my recommendation would be to move to Visual Studio 2010. It’s a great product and makes many things much easier. So at this point, I am going to make the assumption that you have MEF installed one way or the other. I know what they say about assuming, but I’ll live with it.
Managed Extensibility Framework Usage
To begin, we need to look at the basics. You make use of MEF by attributing code as imports and exports. To sum it up simply, you export components you want others to use, and you import components you want to use.
TerminologyIn the Managed Extensibility Framework, your components are called Parts and you deal with them through the use of the CompositionContainer. The Composition Container class is the part container, and it is responsible for matching imports up with associated exports. This is called “Composition.” When the CompositionContainer needs to discover parts available to it, it references a catalog. There are multiple types of catalogs which MEF can examine to find parts. These include assemblies, directories, types and any custom catalogs, such as online services, etc. To combine multiple types of catalogs, the type AggregateCatalog is available. Essentially, at the simplest level, you create a part and attributing it with the Export attribute:
[Export]public SampleComponent{ public void CreateDocument(string name, string data){…}}This marks the component as exportable. Without this attribute, MEF will not be able to find and use your components. In this case, Export infers the type of the component from the class. In the future, we will look at how to specify types when exporting multiple types or using an interface.
A Place for your stuff
In order for MEF to actually find and use your components, it needs to put them somewhere. Of course, it needs to put them somewhere appropriately typed. If you are going to use ONLY a single instance of a component, you can designate it with the [Import] attribute as follows:
public class Client
{
[Import]
public SampleComponent _component { get; set; }
}
This identifies the variable referenced by _component as the place where we want our reference to the component to go. It doesn’t actually load the component. To do that, we will use the CompositionContainer and catalogs as discussed earlier. So the simplest part loader we can build is:
// First, grab our catalog of components from the executing assemble
AggregateCatalog catalog = new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly)));
// Now, let’s create a container
CompositionContainer container = new CompositionContainer(catalog);
// Match up the parts we’ve grabbed from the catalog with our import variable // above
container.ComposeParts(this);
Now, after all this, we can use the _component variable we declared and attributed above, and if everything goes correctly, it should be fully usable:
_component.Methodname();
Ok, so now what we’ve basically done is dynamically loaded a Part. That’s a lot of work for very little payback, but be patient, it gets better
Starting Point
From here, we are going to use MEF to componentize some more code. For this tutorial, we will use a sample payroll project. Initially, it will be pretty simple. To get started, add a reference to System.ComponentModel.Composition to your project. Now, we will start with the following scenario:
We are writing a payroll processing app for the company Widgets.com. Our initial (and simplistic) cut of the application looks like this:
public class Employee
{
public string Name { get; set;}
public decimal HourlyRate { get; set;}
}
public class PayrollManager
{
[Import]
private Payroll Processor _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
{
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);
}
}
Summary
This seems like an awful lot of work for very little payback. But now we can look at what can be improved. First, while we can import our PayrollProcessor from different locations, we only can have one. So if we are providing service to different companies, we can’t have a set of processors, one for each company, unless we run each company separately. Second, we’ve hard coded our classes. This means, that we can only use classes explicitly named PayrollProcessor, and if there is a name collision we are screwed.
To improve this situation, we can use interfaces and export multiple implementations. In the next installment, we will expand this example to allow us to process different payroll rules for each company, and actually create a simple, cleanly extensible system.?
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:
Zend Framework: The Best Framework for Use With Other FrameworksThe Zend Framework, Dependency Injection and Zend_DiZend Framework TutorialManaged Hosting – What’s it all about?Microsoft wants to make Sharing Easier through Live MeshMicrosoft Complicates HTML Emails With Outlook 2007
No comments:
Post a Comment