Sunday, May 16, 2010

Managed Extensibility Framework .Net 4.0

In the past, I have worked with projects that involved extensibility . I have been on projects that lets the functionality of the application to be extended. To achieve this in Microsoft.Net framework we have System.AddIn (AddIn pipeline), VSTA (Visual Studio tools for Application) Applications and Component Model. Open source ventures such as Spring.Net , Unity framework , Structure Map etc.

Previously I have blogged about Unity and AddIn pipeline development to achieve functional extensibility . With the release of .Net 4.0 , new kid is on the block MEF (Managed Extensibility Framework). This framework enables us to write extensible application using the Attribute Programming model, although framework is not restricted to a particular programming model and is itself fully extensible. When I began exploring the framework I found it simple and light weight . There was not that much effort as it took in System.AddIn development, to develop application, from either AddIn developer or hosting application side.

Microsoft them self made Visual Studio 2010 Editor extensible by using MEF. We can write extensions for VS2010 code editor and there are great samples available at CodePlex.

MEF attribute model allows as to extend application by using attributes on the parts, that we want to be composed dynamically. In .Net 4.0 System.Component Model.Composition.dll is the assembly to be referenced to start developing MEF application. Namespaces to be used are : -

using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;

In MEF the component that we want to take part in composition are called parts.Parts are the component on which the attributes are applied. We can apply attribute to the part to make it available for other parts to consume and attribute to request service of parts.

Export attribute is applied on the parts the we want to expose.
Import attribute is applied on the property or constructor of the parts, that wants the services of exported parts. Contracts are the identifiers of the Import and Export attributes. For example :--


public interface IStockService
{
}

[Export(typeof(IStockService))]
public class BSE : IStockService
{
}

[Export(typeof(IStockService))]
public class LSE : IStockService
{
}

[Export(typeof(IStockService))]
public class NSE : IStockService
{
}

public class MyStockApp
{
[ImportMany]
public IEnumerable<IStockService> StockService
{
get;
set;
}
}




.

We can also have multiple imports , all we have to do is mark property by ImportMany.
After we have defined our parts , we need code to initiate the parts and map importers to exporters. To do this we need a catalog to discover the parts , parts can be discovered from a assembly, assemblies in a directory, from specific type . MEF provide various catalogs :- AssemblyCatalog, TypeCatalog, DirectoryCatalog ,AggregateCatalog etc. Next we need container that composes all the parts , when we call compose method.


AssemblyCatalog catalog =
new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly());
CompositionContainer container = new CompositionContainer(catalog);
container.ComposeParts(this);


.

By default all the export parts are singleton, meaning they will have same instance for all the importing parts. But this features can be controlled by partCreationPolicies. We can provide new instance of export to each import.


[Export(typeof(IStockService))]
[PartCreationPolicy(System.ComponentModel.Composition.CreationPolicy.NonShared) ]
//[PartCreationPolicy(System.ComponentModel.Composition.CreationPolicy.Shared)]
public class BSE : IStockService
{
}



.

When we are importing many imports of a Contract , and want to decide which implementation to import , we can use ExportMetadata. ExportMetaData is applied on exports to separate them from other implementation.It takes a key and value as parameter to to its job.


public enum ServiceType
{
Furtures,
Corporate,
Forex
}

[InheritedExport ]
public class ServiceMetaData
{
public const string ServiceType = "ServiceType";
}

[Export(typeof(IStockService))]
[ExportMetadata(ServiceMetaData.ServiceType, ServiceType.Corporate)]
public class LSE : IStockService
{
}



.
After we have categorized our exports , at the importing end, we ImportMany in IEnumerable collection . We use System.Lazy to access the metadata and decide based on metadata which implementation to use. System.Lazy delays the initialization of the parts until the value of the part is actually accessed.



public class MyStockApp
{
[ImportMany]
public IEnumerable < System.Lazy <IStockService,
Dictionary< string,object >>> StockServices
{
get;
set;
}

public IStockService GetService()
{
var service =
from i in this.StockServices
where (ServiceType)i.Metadata[ServiceMetaData.ServiceType]
== ServiceType.Forex
select i.Value;

return (IStockService)service;
}

public void Compose()
{
AssemblyCatalog catalog =
new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly());
CompositionContainer container = new CompositionContainer(catalog);
container.ComposeParts(this);
}
}


.

[InheritedExport] can also be applied to the Export to define that the subclass of this Export will also provide export service. While importing many imports we can define [ImportMany(AllowRecomposition=true)], to define that all the new parts added to directory or assembly will be automatically discovered, composed and updated.

http://msdn.microsoft.com/en-us/magazine/ee291628.aspx

No comments:

Post a Comment