ASP.NET MVC 3: DefaultModelBinder with inheritance/polymorphism

I’ve tried to do something similar before and I came to the conclusion that there’s nothing built in which will handle this.

The option I went with was to create my own model binder (though inherited from the default so its not too much code). It looked for a post back value with the name of the type called xxxConcreteType where xxx was another type it was binding to. This means that a field must be posted back with the value of the type you’re trying to bind; in this case OrderConcreteType with a value of either OrderBottling or OrderFinishing.

Your other alternative is to use UpdateModel or TryUpdateModel and ommit the parameter from your method. You will need to determine which kind of model you’re updating before calling this (either by a parameter or otherwise) and instantiate the class beforehand, then you can use either method to popuplate it

Edit:

Here is the code..

public class AbstractBindAttribute : CustomModelBinderAttribute
{
    public string ConcreteTypeParameter { get; set; }

    public override IModelBinder GetBinder()
    {
        return new AbstractModelBinder(ConcreteTypeParameter);
    }

    private class AbstractModelBinder : DefaultModelBinder
    {
        private readonly string concreteTypeParameterName;

        public AbstractModelBinder(string concreteTypeParameterName)
        {
            this.concreteTypeParameterName = concreteTypeParameterName;
        }

        protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
        {
            var concreteTypeValue = bindingContext.ValueProvider.GetValue(concreteTypeParameterName);

            if (concreteTypeValue == null)
                throw new Exception("Concrete type value not specified for abstract class binding");

            var concreteType = Assembly.GetExecutingAssembly().GetType(concreteTypeValue.AttemptedValue);

            if (concreteType == null)
                throw new Exception("Cannot create abstract model");

            if (!concreteType.IsSubclassOf(modelType))
                throw new Exception("Incorrect model type specified");

            var concreteInstance = Activator.CreateInstance(concreteType);

            bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => concreteInstance, concreteType);

            return concreteInstance;
        }
    }
}

Change your action method to look like this:

public ActionResult Create([AbstractBind(ConcreteTypeParameter = "orderType")] Order order) { /* implementation ommited */ }

You would need to put the following in your view:

@Html.Hidden("orderType, "Namespace.xxx.OrderBottling")

Leave a Comment