views:

296

answers:

2

I've got a question about ModelBinding in ASP.NET MVC (I'm using MVC 2 preview 2) related to inheritance.

Say I have the following interfaces/classes:

interface IBase
class Base : IBase
interface IChild
class Child: Base, IChild

And I have a custom model binder BaseModelBinder.

The following work fine:

ModelBinders.Binders[typeof(Child)] = new BaseModelBinder();
ModelBinders.Binders[typeof(IChild)] = new BaseModelBinder();

The following do not work (on binding a on object of type Child):

ModelBinders.Binders[typeof(Base)] = new BaseModelBinder();
ModelBinders.Binders[typeof(IBase)] = new BaseModelBinder();

Is there any way to have a model binder for a base class that will apply to all inherited classes? I really don't want to have to manually input something for every possible inheriting class.

Also, if it is possible, is there a way to override the modelbinder for a specific inheriting class? Say I got this working but I needed a specific modelbinder for Child2.

Thanks in advance.

+1  A: 

Well, I guess one way of doing it is to sub-class the ModelBindersDictionary class and override the GetBinders(Type modelType, bool fallbackToDefault) method.

public class CustomModelBinderDictionary : ModelBinderDictionary
{
    public override IModelBinder GetBinder(Type modelType, bool fallbackToDefault)
    {
        IModelBinder binder = base.GetBinder(modelType, false);

        if (binder == null)
        {
            Type baseType = modelType.BaseType;
            while (binder == null && baseType != typeof(object))
            {
                binder = base.GetBinder(baseType, false);
                baseType = baseType.BaseType;
            }
        }

        return binder ?? DefaultBinder;
    }
}

Basically walk the class hierarchy until a model binder is found or default to the DefaultModelBinder.

The next step is making the framework accept the CustomModelBinderDictionary. As far as I can tell you need to sub-class the following three classes and override the Binders property: DefaultModelBinder, ControllerActionInvoker and Controller. You might want to provide your own static CustomModelBinders class.

Disclaimer: This is just a rough prototype. I'm not sure if it's actually working, what implications it might have or if it is a sound approach. You might want to download the code for the framework yourself and experiment.

Update:

I guess an alternative solution would be to define your own CustomModelBindingAttribute.

public class BindToBase : CustomModelBinderAttribute
{
    public override IModelBinder GetBinder()
    {
        return new BaseModelBinder();
    }
}

public class CustomController
{
   public ActionResult([BindToBase] Child child)
   {
       // Logic.
   }
}
mrydengren
+2  A: 

I took a simple route, I just register all derived classes dynamically at startup using reflection. Maybe it's not clean solutions, but it's couple of lines in the initialization code that just work ;-)

But if you really want to mess with model binders (you'll HAVE to - eventually - but there're better ways to spend time ;-) you can read this and this.

queen3
I'd say that's an even better solution, with less risk of unwanted side-effects.
mrydengren