ASP.NET MVC ModelBinding Inherited Classes
-
11-09-2019 - |
Question
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.
Solution
I took a simple route, I just register all derived classes dynamically at startup using reflection. Maybe it's not a clean solution, but it's couple of lines in the initialization code that just works ;-)
But if you really want to mess with model binders (you'll HAVE to - eventually - but there're better ways to spend your time ;-) you can read this and this.
OTHER TIPS
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.
}
}