2012-08-31

MEF in Asp.Net MVC 4 and WebApi

,
MEF stands for “Microsoft Extensibility Framework”. If you want an overview of MEF, you can look here. In this post we will integrated MEF with Asp.Net MVC 4 and with the WebApi.

Asp.Net MVC 4

First of all you need to reference the “System.ComponentModel.Composition.dll” assembly which contains the implementation of MEF. Now where are going to create a MEF controller factory. Like the name says, it will be responsible to create the controller that is requested.

public class MefControllerFactory : DefaultControllerFactory
   {
       private readonly CompositionContainer _compositionContainer;

       public MefControllerFactory(CompositionContainer compositionContainer)
       {
           _compositionContainer = compositionContainer;
       }

       protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
       {
           var export = _compositionContainer.GetExports(controllerType, null, null).SingleOrDefault();

           IController result;

           if (null != export)
           {
               result = export.Value as IController;
           }
           else
           {
               result = base.GetControllerInstance(requestContext, controllerType);
               _compositionContainer.ComposeParts(result);
           }
         
           return result;
       }
   }

The MefcontrollerFactory will inherit from the DefaultControllerFactory and has a constructor that accepts a CompositionContainer. To plug in MEF, the GetControllerInstance methode will be overridden. First will look in our exports if something is defined for the requested controller type. Mark that the SingleOrDefault method is used because if more exports are defined for a controller, an exception will be throw. If this exception is thrown, something is wrong with the configuration of the CompositionContainer. If a single export is found, we’ll get the object from the Value property.


If the export isn’t found, the base method will be invoked. That way, the controller is created the default-way. When the controller is created, the ComposeParts method on the CompositionContainer is invoked. This will resolve the needed import for the controller.



This is the object that will be injected. The GetMessage method will return a string that will be shown on the view.
public interface IMyTest{
    String GetMessage();
}

[Export(typeof(IMyTest))]
public class MyTest1 : IMyTest{
    public MyTest1()
    {
        creationDate = DateTime.Now;
    }

    public string GetMessage()
    {
        return String.Format("MyTest1 created at {0}", creationDate.ToString("hh:mm:ss")) ;
    }

    private DateTime creationDate;
}


The Export attribute says to MEF that this class can be exported. The type of IMyTest is pasted as a parameter. This indicates the contract that is used. So when an IMyTest is requested from MEF, an object of MyTest1 is returned.
[Export]
public class HomeController : Controller{
    [Import]
    private IMyTest _myTest;

    public ActionResult Index()
    {
        ViewBag.Message = _myTest.GetMessage();

        return View();
    }
}

The HomeController is marked for export and an import attribute is put on the “_mytest” property. Of course we still need to configure MVC to use our MefControllerFactorty. To accomplish this, is created a static class with one static public method (RegisterMef). This method will create a Mef CompositionContainer and pass this container to the MefControllerFactory. This MefControllerFactory will be set as the controller factory for MVC. To configure the CompositionContainer an AssemblyCatalog is used.
public static class MefConfig{
    public static void RegisterMef()
    {
        var container = ConfigureContainer();

        ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory(container));
        
        var dependencyResolver = System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver;
    }

    private static CompositionContainer ConfigureContainer()
    {
        var assemblyCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
        var container = new CompositionContainer(assemblyCatalog);
        
        return container;
    }
}

The RegisterMef method will be called form the Application_Start method that resides in the global.asax.
public class MvcApplication : System.Web.HttpApplication{
     protected void Application_Start()
     {
        AreaRegistration.RegisterAllAreas();

        MefConfig.RegisterMef();

        WebApiConfig.Register(GlobalConfiguration.Configuration);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
        AuthConfig.RegisterAuth();
     }
}

When the Index action for the HomeController is requested through the browser, everything works just fine but when it is requested it again, an exception is thrown.

A single instance of controller 'MvcApplication5.Controllers.HomeController' cannot be used to handle multiple requests. If a custom controller factory is in use, make sure that it creates a new instance of the controller for each request.


This exception is thrown because the lifetime of the controller is linked to the lifetime of the CompositionContainer. The container keeps a reference to all the objects that it has created. If the same object is requested, the reference of the previous object is given. There are 2 ways to solve this problem.

You could pass an instance of a ComposablePartCatalog (in this case the AssemblyCatalog) to the MefControllerFactorty. Then in the “GetControllerInstance” method always compose a new CompositionContainer to resolve the controller. Personally I'm not really found about that.

The other solution is to use a CreatonPolicy. If we put a CreationPolicy of “NonShared” on the HomeController, a new instance of the HomeController is created from scratch every time it gets requested. Since this a standard feature of MEF, I'll use this solution.
[Export]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class HomeController : Controller{
    [Import]
    private IMyTest _myTest;

    public ActionResult Index()
    {
        ViewBag.Message = _myTest.GetMessage();

        return View();
    }
}

Now we can request the controller as many times as we want.


WebApi


This is the created WebApi controller. Mark that the controller also has a CreationPolicy defined. This is for the same reason a mentioned above.
[Export]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class HomeController : ApiController{
    [Import]
    private IMyTest _myTest;

    public String GetMessage()
    {
        return _myTest.GetMessage();
    }
}

To plug in MEF in the WebApi a object needs to be created from the System.Web.Http.Dependencies.IDependencyResolver interface. This is how the object looks like.
public class MefDependencyResolver : IDependencyResolver{
    private readonly CompositionContainer _container;
    
    public MefDependencyResolver(CompositionContainer container)
    {
        _container = container;
    }

    public IDependencyScope BeginScope()
    {
        return this;
    }

    public object GetService(Type serviceType)
    {
        var export = _container.GetExports(serviceType, null, null).SingleOrDefault();

        return null != export ? export.Value : null;
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        var exports =_container.GetExports(serviceType, null, null);
        var createdObjects = new List<object>();

        if ( exports.Any())
        {
            foreach (var export in exports)
            {
                createdObjects.Add(export.Value);
            }
        }

        return createdObjects;
    }

    public void Dispose()
    {
        ;
    }
}



The “BeginScope” method returns a scope in with the create objects will life. Since we are going to use MEF to control the lifetime of the created objects, we can do a return of “this”.  We can do this because the IDependencyResolver inherits form IDependencyScope. If you really need a limited scope per request, the “BeginScope” method always needs to return a new object of type IDependencyScope.

You also need to implement a “Dispose” method to release the used resources. In the MefDependencyResolver, it isn’t implemented. This is because we return the current instance in the “BeginScope” method. If we disposed the CompositionContainer, we could only request the service once.


Right now, the only thing needed, is the register the create MefDependencyResolver. This will be done in the modified MefConfig class.
public static class MefConfig
   {
       public static void RegisterMef()
       {
           var container = ConfigureContainer();

           ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory(container));
           
           var dependencyResolver = System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver;
           System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver = new MefDependencyResolver(container);
       }

       private static CompositionContainer ConfigureContainer()
       {
           var assemblyCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
           var container = new CompositionContainer(assemblyCatalog);
           
           return container;
       }
   }



The example can be downloaded from here.
Read more →

2012-08-14

Entity Framework: Database Migrations

,

If you don’t like writing SQL DDL statement, like me, than Entity Framework is very handy. With the Code-First, you can write your model and don’t need to worry about your database. It is generated for you by Entity Framework. This is very handy but what if you are working with an existing database? Or if you need to support different versions of your database? That is where database migrations come in to play.

Before we start

  1. Create an Asp.Net MVC 4 application
  2. Use Nuget to install the latest version of the Entity Framework (5.0.0-rc)

Because i am not running an SQL Express or SQL Server on my machine, i am also going to install SqlServerCompact so i can work with an SDF file. This needs a little more configuration.

  1. Install EntityFramework.SqlServerCompact form Nuget.
  2. Change the connectionstring in the web.config
      <connectionStrings>
        <
    add name="DefaultConnection" connectionString="Data Source=|DataDirectory|\DBTest.sdf;Max Database Size=2047" providerName="System.Data.SqlServerCe.4.0" />   
      </
    connectionStrings>

Create your models. In my case i will create as Car and Make class.

namespace EF_DatabaseMigrations.Models
{
public class Car
{
public Int32 Id { get; set; }

public String Model { get; set; }

public Make Make { get; set; }
}

public class Make
{
public Int32 Id { get; set; }

public String Name { get; set; }
}
}

Next i will create a DbDatasource that will map my entities to the database. This datasource will pass the name of the connectionstring to the base constructor.

namespace EF_DatabaseMigrations.DB
{
public class DbDatasource : DbContext
{
public DbDatasource()
: base("DefaultConnection")
{ }

public DbSet<Car> Cars { get; set; }

public DbSet<Make> Makes { get; set; }
}
}

Let’s migrate


To configure your application to use migrations, you have to type “enable-migrations” command in the Package Manager Console. This will create a Migrations folder that contains a Configuration file. This class contains a seed method that accepts the DbDatasource as a parameter. The seed method will always be execute when you update your database. This method will typically be used to create users in the database or in our case to create the Makes in our database.  By default, AutomaticMigrationsEnabled will be set to false. Since we want to automate everything, we need to set it to true :-).


namespace EF_DatabaseMigrations.Migrations
{
internal sealed class Configuration : DbMigrationsConfiguration<EF_DatabaseMigrations.DB.DbDatasource>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
}

protected override void Seed(EF_DatabaseMigrations.DB.DbDatasource context)
{
context.Makes.AddOrUpdate(m => m.Name, new Make { Name = "Ford" }
, new Make { Name = "Opel" }
, new Make { Name = "BMW" });
}
}
}

Because the Seed method is always called, we have to make sure that the Makes we are adding, only are getting inserted if they don’t exists or are updated when they do exists the database. That is why we are using the AddOrUpdate methode. The first parameter is in fact our condition for the insert or update. If a make with a name “Ford” doesn’t exists in the database, it is inserted. If it does exists, it gets updated.


 


When we type “update-database” command in the Package Manager Console, EF will create our database and will call the Seed method. The location of the database will be determined by the connectionstring that you specify. In our case it will be found in the App_Data folder.


When we open the database, we see that a Cars and a Makes table are created. The Makes table is pre-filled.


 image


In the table “_MigrationHistory” all migrations that where performed on this database are stored. That way you can always see what version of database this is. Also Entity Framework will need this information.


Now let’s assume that we want to add a BuildDate property to the Car. We can put this in a new migration. This way we can keep track of all our modifications in our database.


Change the Car entity.


    public class Car
    {
    public Int32 Id { get; set; }

    public String Model { get; set; }

    public Make Make { get; set; }

    public DateTime BuildDate { get; set; }
    }

Add Migration


Type “add-migration AddBuildDate” in the Package Manager Console. This will create a new file in the Migration folder. The file name will contain the date + the name that we specified.


This new file will contain 2 methods that we can override. The “Up” method will contain all new database modifications. The “Down” will contain the rollback of the modifications. This way we can easily “jump” through database versions. We manually add the AddColumn and DropColumn statements.

namespace EF_DatabaseMigrations.Migrations
{
public partial class AddBuildDate : DbMigration
{
public override void Up()
{
AddColumn("dbo.Cars", "BuildDate", c => c.DateTime(nullable: false));
}

public override void Down()
{
DropColumn("dbo.Cars", "BuildDate");
}
}
}

When we want to apply those modifications, we need to type “update-database -TargetMigration:AddBuildDate”. This will modify our database and add the new column.


image


 


If you want to rollback the database at it’s begin state, you type “update-database -TargetMigration:0” in the Package Console Manager.


What about the database adminstrators


We can’t forget these guys. They probably won’t like this way of creating the database and would probably prefer a script. Well you can add an extra parameter to the “update-database” called script that will create a clean SQL script that performs all the necessary steps to create the database.


“update-database –TargetMigration:AddBuildDate –script” will create this script:

ALTER TABLE [Cars] ADD [BuildDate] [datetime]
INSERT INTO [__MigrationHistory] ([MigrationId], [Model], [ProductVersion]) VALUES ('201208140904052_AddBuildDate', 0x1F8B0800000000000400ECBD07601C499625262F6DCA7B7F4AF54AD7E074A10880601324D8904010ECC188CDE692EC1D69472329AB2A81CA6556655D661640CCED9DBCF7DE7BEFBDF7DE7BEFBDF7BA3B9D4E27F7DFFF3F5C6664016CF6CE4ADAC99E2180AAC81F3F7E7C1F3F22FEC7BFF71F7CFC7BBC5B94E9655E3745B5FCECA3DDF1CE4769BE9C56B36279F1D947EBF67CFBE0A3DFE3E8374E1E9FCE16EFD29F34EDF6D08EDE5C369F7D346FDBD5A3BB779BE93C5F64CD78514CEBAAA9CEDBF1B45ADCCD66D5DDBD9D9D83BBBB3B777302F111C14AD3C7AFD6CBB658E4FC07FD79522DA7F9AA5D67E517D52C2F1BFD9CBE79CD50D317D9226F56D934FFECA3D367BFFFD3ACCD2659937F515CD4594B0835E3A74F3E4A8FCB22237C5EE7E5F97B22B7F310C87D64BBA58E4F09C1F6FACDF52AE7CE3FFBE824ABFD06D4E4F7CAAF830FE8A39775B5CAEBF6FA557EAEAF9DCD3E4AEF86EFDDEDBE685FF3DE41CFF4DBB2BDB7F751FA625D96D9A4A40FCEB3B2C93F4A579F3E7ADD5675FE79BECC8904F9EC65D6B679BDC4BB3963AE1478B4FAF47644787877670F44B89B2D9755CB54ED21DE4193E7CA60FABAAD89653E4A9F15EFF2D9F37C79D1CE2DB65F64EFCC27FB3B3BC4395F2D0B62317AABADD7B93F3CF97B73B74FD645392316C84DD7F8FD0D7153844E1D482FB2CBE282C7D61D4AF6965ABFCA4B61A779B1123E1AD3ACFFFEF2E5B3BA5ABCAA00DB7CF6FBBFAED6F51468549D2FDE64F545DE86DD3FBEEB586A23A371773FE2341F4DFCFBB3CD6843F373DC34D5B460349D26F8FD239374BA9CA5430C22982B43119EEBB22D566531A5FE3EFBE85BBDE147401996F240095F86B076C6E3DDEEB8BC11F4198F546F9B1534B73AB8A71368D74610EF6285175EE7ADA343F351EAA8E68FB037A0F055A01E7B5786D479D9C33FE85C05D3FBBA2BB45DA1D93045164333AE1089CD93E2BDAC23EB0A62388608DBD97970B6EFAE183F6324EF0E58C9C75F64AB1589846735F593F4B598CC93EDD7EF6F101702E3EEB489D8458BADED897444769177BEA5AE09D36745DDB4C6667F949ECC16BD66B7E13AD395C77CFD2932FC641AE3777961C86F607EED0072247C46A35A909EE301EAF00C93F4DF4AE1B064655647D4EC4955AE17CB2155BDE96DB5B33E00FDE8F6303CA3E9C3F13EEEC37A7CB743842EB93D8DA92D3B5CDF9DBB5BCDAC48D03732B53125708BB98DBFF6B333B962DAFCF7E5931FFE74842AAA276DAA6D6F4175D7F8164203B5DA214957BDF62971AB796010B1C900216C9FEF818E9A8AAF89CE7B6342EA7156B0DB71D6C071B15ECE2D06D93538FD09EFD99D6E13CB6ED6FE74ECCC63D5F98131626A7443B69E1190261FA534F4CB620603F0FABA69F3C5180DC6AF7F51F93AAF29263DC9C7FB08344DB32FB265719E37ED9BEA6D4EB69EBFFBBA419FF5429B6656FEBF30F22B96ADCACF26CFFBA6806753C4B6BCCCEAE99CDDD1AEDBFC8101D98C7E4778FFFEF8196EF668F0757CF6DE14FEDCC4543F2B7328E6E183A6F01B0C759C5178AFA02480C101C3FB86483447790D126625A9B0A6AD4989F52CE24B0A15A7C52A2B7D6CFBFAFB36F30EAA5970DD6F9EE6AB7C8909F546749B6E3698280BB2C37D378DFB3DC3BDBE7FDE9DA55ECC3714F2895227E99F54349DC27BB1502A1A0C0EC68231A8F118EB67234CF45173AEE10DB1613798FCD98906FBD6979863BDB4695562CAA6B87020E04F2CF369C016B6CDD9F2BC32ECD9C1C834E9AAEABCCD48CF67C7755B9C67D396BE9EE64DC3A9999FCCCA3535395D4CF2D9D9F2CB75BB5AB734E47C3129AF7D6280CB37F5CF216F88F3E32F57EC677E134320340B98AA2F976CC12CDECFFA6A780804C447753BE6B2858EBFB8B6905E54CB5B0252F23D3552FF265FAC281999375F2E5F6797F9306E37D330A4D8E3A74546CEFAA249EF1E09D3787C42EC4739FAA3FF270000FFFFEA4946EC18180000, '5.0.0-rc.net40')


The Visual Studio solution can be downloaded here.

Read more →