2
votes

I have a brand new project, asp.net mvc 3.

It's all pretty standard, using StructureMap and Nhibernate.

I have 3 projetcs, Core, Infrastructure and UI.

The StructureMap wiring is working just fine, the Index action on a Sample controller is working perfectly.

But, on my create view, I've set the @model to be something like

@model Project.Core.Domain.ISample

On the Controller I have a normal post method :

    [HttpPost]
    public ActionResult Create(ISample sample)
    {
        try
        {
            _repo.Save(sample);

            return RedirectToAction("Index");
        }
        catch
        {
            return View();
        }
    }

But I keep getting the "Cannot create an instance of an interface." error.

The last executed lines on the stack are :

[MissingMethodException: Cannot create an instance of an interface.] System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck) +0 System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache) +98 System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipVisibilityChecks, Boolean skipCheckThis, Boolean fillCache) +241 System.Activator.CreateInstance(Type type, Boolean nonPublic) +69 System.Web.Mvc.DefaultModelBinder.CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) +199 System.Web.Mvc.DefaultModelBinder.BindComplexModel(ControllerContext controllerContext, ModelBindingContext bindingContext) +572 System.Web.Mvc.DefaultModelBinder.BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) +449 System.Web.Mvc.ControllerActionInvoker.GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor) +317

I would expect that mvc would use the DependencyResolver internally, if it did, it would be able to create a concrete instance of my ISample interface...but there's a big chance that I understood something completely wrong and this makes no sense...

If I make this simple change to the controller, everything works normally :

public ActionResult Create(Sample sample)

I may be wrong, but this seems wrong to me, everything else is able to use the interface to communicate, why would I have to use the concrete class on the Create action ? This would take away some of the flexibility that the interface gave me.

Does anyone have any idea on how to proceed or if I'm on the wrong track ?

Thanks for your attention.

This is how I got to what I wanted with Darin's help

I've created a new GenericModelBinder (maybe the name could be better)

    public class GenericModelBinder : DefaultModelBinder
{
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        var obj = DependencyResolver.Current.GetService(modelType);
        return base.CreateModel(controllerContext, bindingContext, obj.GetType());
    }
}

And in global.asax I've added :

ModelBinders.Binders.DefaultBinder = new GenericModelBinder();

Thanks for your help!

1

1 Answers

0
votes

You will need to write a custom model binder for the ISample type for this to work. ASP.NET MVC uses the default model binder when it invokes a controller action in order to instantiate the action parameters from the request values.

public class MyISampleModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        // Here you need to return the proper instance of the ISample interface
        // based on the request values or some other rules you need
    }
}

and then in Application_Start register this binder:

ModelBinders.Binders.Add(typeof(ISample), new MyISampleModelBinder());