带有List <BaseClass>的ViewModel和编辑器模板

37 asp.net-mvc model-binding viewmodel asp.net-mvc-3

我有一个视图,列出要添加到平面图的表.从表中获得TableInputModel允许RectangleTableInputModel,CircleTableInputModel

ViewModel有一个列表,TableInputModel其中包含所有派生类型.

我对每个派生类型都有局部视图,并且给定了一个List混合派生类型,框架知道如何渲染它们.

但是,在提交表单时,类型信息会丢失.我尝试使用自定义模型绑定器,但因为类型信息在提交时丢失了,所以它不会工作...

有人曾尝试过这个吗?

Dar*_*rov 69

假设您有以下型号:

public abstract class TableInputModel 
{ 

}

public class RectangleTableInputModel : TableInputModel 
{
    public string Foo { get; set; }
}

public class CircleTableInputModel : TableInputModel 
{
    public string Bar { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

以下控制器:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var model = new TableInputModel[]
        {
            new RectangleTableInputModel(),
            new CircleTableInputModel()
        };
        return View(model);
    }

    [HttpPost]
    public ActionResult Index(TableInputModel[] model)
    {
        return View(model);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在你可以写视图了.

主要观点Index.cshtml:

@model TableInputModel[]
@using (Html.BeginForm())
{
    @Html.EditorForModel()
    <input type="submit" value="OK" />
}
Run Code Online (Sandbox Code Playgroud)

和相应的编辑器模板.

~/Views/Home/EditorTemplates/RectangleTableInputModel.cshtml:

@model RectangleTableInputModel
<h3>Rectangle</h3>
@Html.Hidden("ModelType", Model.GetType())
@Html.EditorFor(x => x.Foo)
Run Code Online (Sandbox Code Playgroud)

~/Views/Home/EditorTemplates/CircleTableInputModel.cshtml:

@model CircleTableInputModel
<h3>Circle</h3>
@Html.Hidden("ModelType", Model.GetType())
@Html.EditorFor(x => x.Bar)
Run Code Online (Sandbox Code Playgroud)

最后遗漏的难题是该TableInputModel类型的自定义模型绑定器,它将使用已发布的隐藏字段值来获取类型并实例化正确的实现:

public class TableInputModelBinder : DefaultModelBinder
{
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        var typeValue = bindingContext.ValueProvider.GetValue(bindingContext.ModelName + ".ModelType");
        var type = Type.GetType(
            (string)typeValue.ConvertTo(typeof(string)), 
            true
        );
        var model = Activator.CreateInstance(type);
        bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, type);
        return model;
    }
}
Run Code Online (Sandbox Code Playgroud)

将在以下地址注册Application_Start:

ModelBinders.Binders.Add(typeof(TableInputModel), new TableInputModelBinder());
Run Code Online (Sandbox Code Playgroud)

这就是全部.现在,在Index Post操作中,模型数组将使用正确的类型正确初始化.

  • 仅为记录:`bindingContext.ModelName`可能为空,因此在这种情况下你必须删除Propertyname之前的点. (5认同)