You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Framework MVC中如何HTTP POST动态长度的派生模型集合?

问题场景

现有一组继承自基类Shape的派生类模型:

public class Shape
{
    public string Color { get; set; }
}

public class Circle : Shape
{
    public int Radius { get; set; }
}

public class Triangle : Shape
{
    public string Type { get; set; }  // 例如"isosceles"、"equilateral"等
}

控制器通过MyShapes Action返回包含多种形状的集合视图:

[HttpGet]
public ActionResult MyShapes()
{
    List<Shape> listOfShapes = GetShapesFromDatabase();
    return View(listOfShapes);
}

当前视图通过判断类型渲染分部视图,但回发时只能通过AJAX序列化数据,不符合MVC原生模式。需要实现原生表单提交,让控制器正确接收修改后的不同类型形状集合。

解决方案:基于EditorTemplates与自定义ModelBinder的实现

1. 创建派生类的EditorTemplates

在Views/Shared/EditorTemplates目录下,为每个派生类创建对应的模板文件,模板中需包含类型标识隐藏字段,用于后续ModelBinder识别具体类型:

Circle.cshtml

@model Circle
<div class="shape-item">
    @Html.Hidden("[$index].ShapeType", "Circle")
    @Html.LabelFor(m => m.Color)
    @Html.EditorFor(m => m.Color)
    @Html.LabelFor(m => m.Radius)
    @Html.EditorFor(m => m.Radius)
</div>

Triangle.cshtml

@model Triangle
<div class="shape-item">
    @Html.Hidden("[$index].ShapeType", "Triangle")
    @Html.LabelFor(m => m.Color)
    @Html.EditorFor(m => m.Color)
    @Html.LabelFor(m => m.Type)
    @Html.EditorFor(m => m.Type)
</div>

2. 修改主视图,自动匹配EditorTemplate

使用Html.EditorFor(m => m)替代原有的foreach判断,MVC会根据集合中每个元素的类型自动加载对应的模板:

@model List<Shape>

@using (Html.BeginForm("SaveShapes", "Shape", FormMethod.Post))
{
    <p>修改形状。</p>
    @Html.EditorFor(m => m)
    
    <button type="button" id="add-shape">添加新形状</button>
    <button type="submit">保存更改</button>
}

<!-- 隐藏的新形状模板容器,用于动态添加 -->
<div id="shape-templates" style="display:none;">
    <div id="circle-template">
        @Html.EditorFor(m => new Circle())
    </div>
    <div id="triangle-template">
        @Html.EditorFor(m => new Triangle())
    </div>
</div>

3. 实现自定义ModelBinder

创建ShapeModelBinder,通过隐藏字段的ShapeType值实例化对应的派生类,确保MVC能正确绑定集合中的不同类型:

public class ShapeModelBinder : DefaultModelBinder
{
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        var shapeTypeValue = bindingContext.ValueProvider.GetValue(bindingContext.ModelName + ".ShapeType");
        if (shapeTypeValue != null)
        {
            string shapeType = shapeTypeValue.AttemptedValue;
            // 替换为你的命名空间和程序集名称
            Type concreteType = Type.GetType($"YourNamespace.{shapeType}, YourAssembly");
            if (concreteType != null && typeof(Shape).IsAssignableFrom(concreteType))
            {
                var model = Activator.CreateInstance(concreteType);
                bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, concreteType);
                return model;
            }
        }
        return base.CreateModel(controllerContext, bindingContext, modelType);
    }
}

在全局注册该ModelBinder(Global.asax或Startup.cs):

// Global.asax Application_Start方法内
ModelBinders.Binders.Add(typeof(Shape), new ShapeModelBinder());

4. 控制器接收Action

现在可以直接接收List<Shape>参数,集合中会自动包含正确类型的实例:

[HttpPost]
public ActionResult SaveShapes(List<Shape> shapes)
{
    // 处理数据库更新逻辑
    UpdateShapesInDatabase(shapes);
    return RedirectToAction("MyShapes");
}

5. 动态添加新形状的前端处理

添加JS代码实现动态克隆模板并替换索引,确保新添加的形状能正确回发:

document.getElementById('add-shape').addEventListener('click', function() {
    var shapeType = prompt("选择要添加的形状类型:Circle/Triangle");
    if (!shapeType) return;
    
    var templateId = shapeType.toLowerCase() + "-template";
    var template = document.getElementById(templateId).innerHTML;
    var shapesContainer = document.querySelector('.shape-item').parentNode;
    var index = document.querySelectorAll('.shape-item').length;
    
    // 替换模板中的[$index]为当前集合长度(新元素的索引)
    var newShapeHtml = template.replace(/\[\$index\]/g, `[${index}]`);
    shapesContainer.insertAdjacentHTML('beforeend', newShapeHtml);
});

内容的提问来源于stack exchange,提问作者RotundChinchilla

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 04:11:04