[Special Note: I have just released SP1 Preview which contains the newly added Autofac Adapter and minor Windsor changes]
In this post, I will show you how you can register your custom services in your preferred IoC container and how the MvcExtensions will integrate it with the rest of the application. One of the goal of MvcExtensions is, unlike the other mvc add-on library which provides its own custom interface for registration/resolution, it will let you use your container syntax that you are familiar with. In case, if you have to refer your container in your application you should use the CommonServiceLocator(or CSL in short) which is now a days a standard to abstract your underlying container from the rest of the application. Yes, I know some of the ASP.NET MVC expert is saying that CSL is not adequate, but let me remind them, the goal of the CSL is laid into its name, if it suppose to give service registration or nested container support, it should be named as Common Service Registrar/Container Factory or something similar. Beside that referring your Container/Locator other than the bootstrapping phase is considered to be a design smell. Although behind the scene, the MvcExtensions extends the CSL with IServiceRegistrar and IServiceInjector interface but you never have to refer those in your code. MvcExtensions comes with most of the popular IoC container adapters that includes Autofac(added in the SP1 Preview), Ninject, StructureMap, Unity and Windsor.
Lets take a fictitious blogging application as an example to see how the registration works and assume that blogging application contains Database, DatabaseFactory, UnitOfWork, Repository, few Domain Services and Mappers(converts domain objects into data transfer objects) as custom services.
Lets start with the Ninject, first you have to add reference of Ninject 2, which you can find from Ninject official site, next you have to add reference of CSL and at last the MvcExtensions and the Ninject Adapter. Next, we will create a Ninject Module which is the standard way of registering services in Ninject. The following shows the module that registers all of the above services:
Ninject
namespace MyBlog
{
using Ninject;
using Ninject.Modules;
public class NinjectServiceRegistrationModule : NinjectModule
{
public override void Load()
{
Bind<IDatabaseFactory>().To<DatabaseFactory>().InRequestScope();
Bind<IDatabase>().ToMethod(c => c.Kernel.Get<IDatabaseFactory>().Get());
Bind<IUnitOfWork>().To<UnitOfWork>();
Bind(typeof(IRepository<>)).To(typeof(Repository<>));
Bind<IMapper<Blog, BlogInfo>>().To<BlogMapper>();
Bind<IMapper<Post, PostInfo>>().To<PostMapper>();
Bind<IMapper<Tag, TagInfo>>().To<TagMapper>();
Bind<IMapper<Comment, CommentInfo>>().To<CommentMapper>();
Bind<IBlogService>().To<BlogService>();
Bind<IPostService>().To<PostService>();
Bind<ITagService>().To<TagService>();
Bind<ICommentService>().To<CommentService>();
}
}
}
As you can see there is no nothing special from MvcExtensions, you are creating the Ninject Module in the same way as you do where MvcExtensions is not used. Behind the scene, The MvcExtensions will scan all the available assemblies of your application and register those Ninject Modules in the Kernel. Also check that we are not registering anything specific to ASP.NET MVC, those are handled by the BootstrapperTask which I have discussed in my last post.
Now, lets see how we can use StructureMap to register the above services. First, we will add reference of StructureMap 2.6.1, then we will add reference of CSL, MvcExtensions, StructureMap Adapter. Next, we will create a Registry which serves same as Ninject Module. The following shows the StructureMap registration:
StructureMap
namespace MyBlog
{
using StructureMap.Configuration.DSL;
public class StructureMapServiceRegistry : Registry
{
public StructureMapServiceRegistry()
{
For<IDatabaseFactory>().HttpContextScoped().Use<DatabaseFactory>();
For<IDatabase>().Use(c => c.GetInstance<IDatabaseFactory>().Get());
For<IUnitOfWork>().Use<UnitOfWork>();
For(typeof(IRepository<>)).Use(typeof(Repository<>));
For<IMapper<Blog, BlogInfo>>().Use<BlogMapper>();
For<IMapper<Post, PostInfo>>().Use<PostMapper>();
For<IMapper<Tag, TagInfo>>().Use<TagMapper>();
For<IMapper<Comment, CommentInfo>>().Use<CommentMapper>();
For<IBlogService>().Use<BlogService>();
For<IPostService>().Use<PostService>();
For<ITagService>().Use<TagService>();
For<ICommentService>().Use<CommentService>();
}
}
}
I know, we can use the StructureMap convention based api to make the above registration much more simpler and short, but that is a different topic. Same as Ninject, the MvcExtensions will scan for the SM registry and automatically register those in the container.
Next, the Autofac (it has been re-added in the SP1 Preview), Autofac also has a Module which acts same as Ninject Module and StructureMap Registry. Before creating the Module, you have add reference of Autofac 2.2.3 Preview, CSL, MvcExtensions and Autofac Adapter. The following shows the Autofac registration:
Autofac
namespace MyBlog
{
using Autofac;
public class AutofacServiceRegistrationModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<DatabaseFactory>().As<IDatabaseFactory>().PerRequestScoped();
builder.Register(c => c.Resolve<IDatabaseFactory>().Get()).As<IDatabase>();
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>();
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>));
builder.RegisterType<BlogMapper>().As<IMapper<Blog, BlogInfo>>();
builder.RegisterType<PostMapper>().As<IMapper<Post, PostInfo>>();
builder.RegisterType<TagMapper>().As<IMapper<Tag, TagInfo>>();
builder.RegisterType<CommentMapper>().As<IMapper<Comment, CommentInfo>>();
builder.RegisterType<BlogService>().As<IBlogService>();
builder.RegisterType<PostService>().As<IPostService>();
builder.RegisterType<TagService>().As<ITagService>();
builder.RegisterType<CommentService>().As<ICommentService>();
}
}
}
As you can see In line-9 I am using the PerRequestScoped, this is an extension method that has been added in the Autofac Adapter, behind the scene it creates a new scope when a new request begins, sets up the current service locator and all the wacky stuffs that you like to avoid.
Now lets see the most widely used container the Windsor, Windsor has IWindsorInstaller (which I think has not been highlighted that much) which can be used for modular registration. To create a new installer you have add reference of the Windsor 2.1 binaries, CSL, MvcExtensions and Windsor adapter. The following shows the installer which register the services in the container:
Windsor
namespace MyBlog
{
using Castle.MicroKernel;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
public class WindsorServiceInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Component.For<IDatabaseFactory>().ImplementedBy<DatabaseFactory>().LifeStyle.PerWebRequest)
.Register(Component.For<IDatabase>().UsingFactoryMethod(k => k.Resolve<IDatabaseFactory>().Get()))
.Register(Component.For<IUnitOfWork>().ImplementedBy<UnitOfWork>());
container.Register(Component.For(typeof(IRepository<>)).ImplementedBy(typeof(Repository<>)));
container.Register(Component.For<IMapper<Blog, BlogInfo>>().ImplementedBy<BlogMapper>())
.Register(Component.For<IMapper<Post, PostInfo>>().ImplementedBy<PostMapper>())
.Register(Component.For<IMapper<Tag, TagInfo>>().ImplementedBy<TagMapper>())
.Register(Component.For<IMapper<Comment, CommentInfo>>().ImplementedBy<CommentMapper>());
container.Register(Component.For<IBlogService>().ImplementedBy<BlogService>())
.Register(Component.For<IPostService>().ImplementedBy<PostService>())
.Register(Component.For<ITagService>().ImplementedBy<TagService>())
.Register(Component.For<ICommentService>().ImplementedBy<CommentService>());
}
}
}
I am using the new syntax but you can also use the regular one, when the container is created the ArrayResolver and FactorySupportFacility are added, so you do not have to add those, if you need any other facilities you can add those creating new installer.
And at last, the Unity. Unlike the other containers the Unity does not have the Modular registration support . This is the reason Unity Adapter comes with an special interface IModule which you have to implement in order to support the modular registration. Let us see the example first, like above you have add to reference of Unity 2, CSL, MvcExtensions and Unity Adapter. Next, create a class which implements the IModule and override the Load method like the following.
Unity
namespace MyBlog
{
using Microsoft.Practices.Unity;
using MvcExtensions.Unity;
public class UnityServiceRegistrationModule : IModule
{
public void Load(IUnityContainer container)
{
container.RegisterType<IDatabaseFactory, DatabaseFactory>(new PerRequestLifetimeManager())
.RegisterType<IDatabase, Database>(new InjectionFactory(c => c.Resolve<IDatabaseFactory>().Get()))
.RegisterType<IUnitOfWork, UnitOfWork>();
container.RegisterType(typeof(IRepository<>), typeof(Repository<>));
container.RegisterType<IMapper<Blog, BlogInfo>, BlogMapper>()
.RegisterType<IMapper<Post, PostInfo>, PostMapper>()
.RegisterType<IMapper<Tag, TagInfo>, TagMapper>()
.RegisterType<IMapper<Comment, CommentInfo>, CommentMapper>();
container.RegisterType<IBlogService, BlogService>()
.RegisterType<IPostService, PostService>()
.RegisterType<ITagService, TagService>()
.RegisterType<ICommentService, CommentService>();
}
}
}
And like other adapters the MvcExtensions will scan all the assemblies for IModule and pass the container for your registration. One special thing in line-11, for IDatabaseFactory we are using PerRequestLifetimeManager which does not exists in the actual Unity. The issue is like the other containers the Unity does not have any built-in lifetime management support which returns the same service in the same request no matter how many times it has been requested. But it does have the support to create custom lifetime managers, the PerRequestLifetimeManager is a custom class which is included in the Unity adapter.
You will find the above code in the bottom of this post. Just make sure the HttpApplication inherits from you preferred container’s HttpApplication, currently it is using Autofac.
In conclusion, as you have seen working with MvcExtensions adapters is nothing different working with the original container. In the next post, we will see how to use the PerRequestTask and in which scenario it is suitable.
So, stay tuned.
Download: MyBlog.zip