Showing posts with label ASP.Net MVC. Show all posts
Showing posts with label ASP.Net MVC. Show all posts

Friday, August 12, 2011

Register ViewModels with Autofac by overriding DefaultModelBinder

Autofac is a great IoC framework and we all know that. I however had a minor problem when registering ViewModels for my ASP.Net MVC application. If you register your viewmodels like a regular assembly or regular types, it will work perfect only if your viewmodel does not have any dependency or it has a parameter less constructor.

Here’s the example:

public class CustomerController : Controller
{
public ActionResult Index(CustomerViewModel viewModel)
{
return View(viewModel);
}


}


public class CustomerViewModel
{
ICustomerRepository repository;
public CustomerViewModel(ICustomerRepository repository)
{
this.repository = repository;
GetAllCustomers();
}

private void GetAllCustomers()
{
//Do something
}
}




if you’ve registered CustomerViewModel as following then this will throw a runtime error.



builder.RegisterType<CustomerViewModel>();



 



This does not work because Autofac will not intercept when CustomerViewModel will be created by DefaultModelBinder.



Here’s a workaround:



Create a new CustomeModelBinder class which will inherit from DefaultModelBinder and override the CreateModel method. Using DependencyResolver class, Autofac will now intercept and resolves dependency of viewmodels.



public class CustomModelBinder<T> : DefaultModelBinder where T : class
{
protected override object CreateModel(ControllerContext controllerContext,
ModelBindingContext bindingContext, Type modelType)
{
var item = DependencyResolver.Current.GetService<T>();

if (item != null)
{
return item;
}
throw new ArgumentException(string.Format("model type {0} is not registered", modelType.Name));
}
}


 



Now a simple extension method to Register all viewmodels (assuming the convention is have all viewmodel class name ends with “ViewModel”)



 



public static void RegisterViewModels(this ContainerBuilder builder)
{
var types = System.Reflection.Assembly.GetExecutingAssembly().GetTypes().Where(x => x.Name.EndsWith("ViewModel", StringComparison.OrdinalIgnoreCase));
types.ToList().ForEach(modelType =>
{
Type modelBinderType = typeof(CustomModelBinder<>);
Type[] typeArgs = { modelType };
Type genType = modelBinderType.MakeGenericType(typeArgs);
object viewModel = Activator.CreateInstance(genType);
ModelBinders.Binders.Add(modelType, (IModelBinder)viewModel);
builder.RegisterType(modelType).InstancePerDependency();
});
}


Alternatively, if all your viewmodels inherit from a common base class (say, BaseViewModel) then following extension can be handy.



public static void RegisterBaseViewModelDescendants<T>(this ContainerBuilder builder) where T : BaseViewModel
{
var types = System.Reflection.Assembly.GetExecutingAssembly().GetTypes().Where(x => x.IsSubclassOf(typeof(T)));
types.ToList().ForEach(modelType =>
{
Type modelBinderType= typeof(IocModelBinder<>);
Type[] typeArgs = { modelType };
Type genType = modelBinderType.MakeGenericType(typeArgs);
object viewmodel = Activator.CreateInstance(genType);
ModelBinders.Binders.Add(modelType, (IModelBinder)viewmodel);
builder.RegisterType(modelType).InstancePerDependency();
});
}