DefaultModelBinder and set of inherited objects

I have an action method similar below.

[AcceptVerbs(HttpVerbs.Post)] public ActionResult Create(Form newForm) { ... } 

I have a model with the following classes that I would like to load data from ajax JSON data.

 public class Form { public string title { get; set; } public List<FormElement> Controls { get; set; } } public class FormElement { public string ControlType { get; set; } public string FieldSize { get; set; } } public class TextBox : FormElement { public string DefaultValue { get; set; } } public class Combo : FormElement { public string SelectedValue { get; set; } } 

Here are the JSON data.

 { "title": "FORM1", "Controls": [ { "ControlType": "TextBox", "FieldSize": "Small" ,"DefaultValue":"test"}, { "ControlType": "Combo", "FieldSize": "Large" , "SelectedValue":"Option1" } ] } $.ajax({ url: '@Url.Action("Create", "Form")', type: 'POST', dataType: 'json', data: newForm, contentType: 'application/json; charset=utf-8', success: function (data) { var msg = data.Message; } }); 

DefaultModelBinder processes the structure of nested objects, but cannot resolve various subclasses.

What would be the best way to load a List with the appropriate subclasses?

+7
source share
1 answer

I looked at the mvc DefaultModelBinder implementation code. When binding the DefaultModelBinder model, view the model properties using the GetModelProperties () method. The following shows how DefaultModelBinder looks for properties:

  protected virtual ICustomTypeDescriptor GetTypeDescriptor(ControllerContext controllerContext, ModelBindingContext bindingContext) { return TypeDescriptorHelper.Get(bindingContext.ModelType); } 

TypeDescriptorHelper.Get uses ModelType, which is a partent type (in my case, FormElement), so the properties of the child class (TextBox, Combo) are not retrieved.

You can override the method and change the behavior to get a specific child type, as shown below.

 protected override System.ComponentModel.PropertyDescriptorCollection GetModelProperties(ControllerContext controllerContext, ModelBindingContext bindingContext) { Type realType = bindingContext.Model.GetType(); return new AssociatedMetadataTypeTypeDescriptionProvider(realType).GetTypeDescriptor(realType).GetProperties(); } protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) { ValueProviderResult result; result = bindingContext.ValueProvider.GetValue(bindingContext.ModelName + ".ControlType"); if (result == null) return null; if (result.AttemptedValue.Equals("TextBox")) return base.CreateModel(controllerContext, bindingContext, typeof(TextBox)); else if (result.AttemptedValue.Equals("Combo")) return base.CreateModel(controllerContext, bindingContext, typeof(Combo)); return null; } 
+1
source

All Articles