public ActionResult EmployeeList(Employee employee) { return View(); }
One down side of this is that during unit test you have to construct an object from employee class. It will be nice if we can have interface parameter type something like this. In this case, unit test doesn't have to bother on the concrete implementation of Employee class.
public ActionResult EmployeeList(IEmployee employee) { return View(); }
But unfortunately this will throw exception "Cannot create an instance of an interface". The reason for this is that during binding it try to create an instance of model type which in this case is an interface and hence it throws exception. You can easily decompile System.Web.Mvc.DefaultModelBinder and look at method CreateModel where this is failing.
To get away with the issue I created an InterfaceTypeModelBinder. The constructor of the InterfaceTypeModelBinder takes ModelType as aparameter.
public class InterfaceTypeModelBinder : DefaultModelBinder { private Type ModelType { get; set; } public InterfaceTypeModelBinder(Type modelType) { ModelType = modelType; } protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) { Type type = GetModelType(bindingContext.ModelType); Debug.WriteLine(Environment.StackTrace); return Activator.CreateInstance(type); } private Type GetModelType( Type modelType) { Type type = modelType; if (ModelType != null) type = ModelType; if (modelType.IsGenericType) { Type genericTypeDefinition = type.GetGenericTypeDefinition(); if (genericTypeDefinition == typeof(IDictionary<,>)) { type = typeof(Dictionary<,>).MakeGenericType(type.GetGenericArguments()); } else { if (genericTypeDefinition == typeof(IEnumerable<>) || genericTypeDefinition == typeof(ICollection<>) || genericTypeDefinition == typeof(IList<>)) { type = typeof(List<>).MakeGenericType(type.GetGenericArguments()); } } } return type; } }Now in my Global.asax.cs file I can register my binder like this
protected void Application_Start()
{ AreaRegistration.RegisterAllAreas(); RegisterGlobalFilters(GlobalFilters.Filters); RegisterRoutes(RouteTable.Routes); ModelBinders.Binders[typeof(IEmployee)] = new InterfaceTypeModelBinder(typeof(Employee)); ModelBinders.Binders[typeof(ICustomer)] = new InterfaceTypeModelBinder(typeof(Customer)); }
By doing this I can use IEmployee or ICustomer or any type as a parameter as long as it is registered.
No comments:
Post a Comment