DefaultModelBinder和继承对象的集合

Thu*_*ein 7 c# model-view-controller asp.net-mvc modelbinders

我有一个这样的动作方法如下.

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Form newForm)
{
     ...
}
Run Code Online (Sandbox Code Playgroud)

我有一个包含以下类的模型,我想从ajax JSON数据加载数据.

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; }
}
Run Code Online (Sandbox Code Playgroud)

这是JSON数据.

{ "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;
                }
            });
Run Code Online (Sandbox Code Playgroud)

DefaultModelBinder正在处理嵌套对象结构,但它无法解析不同的子类.

使用相应的子类加载List的最佳方法是什么?

Thu*_*ein 2

我查看了 mvc DefaultModelBinder 实现的代码。绑定模型时 DefaultModelBinder 使用 GetModelProperties() 查找模型的属性。以下是 DefaultModelBinder 如何查找属性:

 protected virtual ICustomTypeDescriptor GetTypeDescriptor(ControllerContext controllerContext, ModelBindingContext bindingContext) {
            return TypeDescriptorHelper.Get(bindingContext.ModelType);
        }
Run Code Online (Sandbox Code Playgroud)

TypeDescriptorHelper.Get 使用 ModelType,它是父类型(在我的例子中为 FormElement),因此不会检索子类(TextBox、Combo)的属性。

您可以重写该方法并更改行为以检索特定子类型,如下所示。

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;
        }
Run Code Online (Sandbox Code Playgroud)