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

如何将HTML表格多行数据存入数据库?解决描述列覆盖问题

批量保存表格记录时Description字段被覆盖的解决方案

问题场景

在将HTML表格中的成员考勤记录批量保存到数据库时,所有记录的Description字段都被第一条输入的值覆盖,其他字段(如MemberId)能正常对应到每一行,但描述无法实现行级独立存储。

原代码问题分析

当前实现的核心问题在于:

  • ViewModel中Description是单个字符串属性,无法存储多行独立的描述内容;
  • 页面中所有行的Description输入框绑定到同一个模型属性,表单提交时只有最后一个(或第一个)输入值会被绑定,导致循环保存时所有记录复用同一个值。

原ViewModel代码

public SelectList Meetings { get; set; } 
public string MeetingId { get; set; } 
public string Description { get; set; }
public bool IsPresent { get; set; }
public DateTime Date { get; set; }
public List<SelectListItem> Members { get; set; }
public string MemberId { get; set; }

原View代码(核心部分)

<tbody>
    @for (int i = 0; i < Model.Members.Count(); i++)
    {
        <tr>
            <td>@Model.Members[i].Text</td>
            <td><input asp-for="@Model.Description" id="desc"/></td>
            <td>
                <input type="hidden" class="custom-checkbox" asp-for="@Model.Members[i].Value" />
                <label class="a8z-toggle-switch" data-size="sm" data-color="blue" data-text="true" data-style="rounded">
                    <input asp-for="Members[i].Selected" type="checkbox" id="example_default_1">
                    <span class="toggle">
                        <span class="switch"></span>
                    </span>
                </label>
            </td>
        </tr>
    }
</tbody>

原Controller代码(核心部分)

[HttpPost]
public IActionResult AddAttendance(AddAttendanceVM addAttendanceVM)
{
    foreach (var att in addAttendanceVM.Members)
    {
        Attendance addThisAttendance = new()
        {
            Date = addAttendanceVM.Date,
            Description = addAttendanceVM.Description,
            MemberId = att.Value,
            MeetingId = addAttendanceVM.MeetingId,
            IsPresent = addAttendanceVM.IsPresent,
            CreatedBy = User.Claims.FirstOrDefault(c => c.Type == "Name").Value,
            CreatedDate = DateTime.Now
        };
        xct.Attendances.Add(addThisAttendance); 
    }
    xct.SaveChanges();
    return ViewComponent(nameof(AttendanceLists));
}

解决方案

通过拆分模型为主ViewModel+子模型的结构,实现行级数据的独立绑定与存储。

1. 重构ViewModel

新增子模型存储单条成员考勤数据,主ViewModel用列表承载所有子模型:

// 子模型:存储单条成员的考勤信息
public class MemberAttendanceItem
{
    public string MemberId { get; set; }
    public string MemberName { get; set; }
    public string Description { get; set; }
    public bool IsPresent { get; set; }
}

// 主ViewModel:存储全局数据与所有成员考勤列表
public class AddAttendanceVM
{
    public SelectList Meetings { get; set; } 
    public string MeetingId { get; set; } 
    public DateTime Date { get; set; }
    public List<MemberAttendanceItem> MemberAttendances { get; set; } = new List<MemberAttendanceItem>();
}

2. 更新View页面

遍历子模型列表,使用带索引的绑定方式(asp-for="@Model.MemberAttendances[i].Description")确保每个输入框的name属性唯一,实现行级数据独立提交:

@model AddAttendanceVM

@using (Html.BeginForm("AddAttendance","Attendance",FormMethod.Post))
{
    <div class="card-body">
        <div class="form-group">
            <input type="date" asp-for="@Model.Date" class="form-control js-datepicker" asp-format="{0:dd/MMM/yyyy}">
        </div>
        
        <div class="form-group">
            <select type="text" id="meetingType" class="select2 form-control" asp-items="@Model.Meetings" asp-for="@Model.MeetingId" style="width:100%" multiple></select>
        </div>
        <div class="table-responsive">
            <table class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
                <thead>
                    <tr>
                        <th>Name</th>
                        <th>Description</th>
                        <th>Action</th>
                    </tr>
                </thead>
                <tbody>
                    @for (int i = 0; i < Model.MemberAttendances.Count(); i++)
                    {
                        <tr>
                            <td>@Model.MemberAttendances[i].MemberName</td>
                            <td><input asp-for="@Model.MemberAttendances[i].Description" class="form-control"/></td>
                            <td>
                                <input type="hidden" asp-for="@Model.MemberAttendances[i].MemberId"/>
                                <label class="a8z-toggle-switch" data-size="sm" data-color="blue" data-text="true" data-style="rounded">
                                    <input asp-for="@Model.MemberAttendances[i].IsPresent" type="checkbox">
                                    <span class="toggle">
                                        <span class="switch"></span>
                                    </span>
                                </label>
                            </td>
                        </tr>
                    }
                </tbody>
            </table>
        </div>
        <button type="submit" class="btn btn-primary">保存考勤</button>
    </div>
}

3. 调整Controller逻辑

在POST方法中遍历子模型列表,将每个子模型的数据映射到数据库实体,实现每条记录的独立保存:

[HttpPost]
public IActionResult AddAttendance(AddAttendanceVM addAttendanceVM)
{
    if (!ModelState.IsValid)
    {
        // 验证失败时重新加载页面数据(需补充会议选项与成员数据的加载逻辑)
        // addAttendanceVM.Meetings = new SelectList(...);
        return View(addAttendanceVM);
    }

    var createdBy = User.Claims.FirstOrDefault(c => c.Type == "Name")?.Value;
    foreach (var item in addAttendanceVM.MemberAttendances)
    {
        // 可根据业务需求过滤需要保存的记录(比如仅保存已勾选的考勤)
        if (item.IsPresent)
        {
            var attendance = new Attendance
            {
                Date = addAttendanceVM.Date,
                Description = item.Description,
                MemberId = item.MemberId,
                MeetingId = addAttendanceVM.MeetingId,
                IsPresent = item.IsPresent,
                CreatedBy = createdBy,
                CreatedDate = DateTime.Now
            };
            xct.Attendances.Add(attendance);
        }
    }
    xct.SaveChanges();
    return ViewComponent(nameof(AttendanceLists));
}

补充:GET方法数据初始化

在返回页面的GET方法中,需要将成员数据填充到MemberAttendances列表:

public IActionResult AddAttendance()
{
    var vm = new AddAttendanceVM();
    // 加载会议下拉选项
    vm.Meetings = new SelectList(/* 会议数据源, "Value字段", "Text字段" */);
    // 加载成员数据并填充到子模型列表
    var members = /* 从数据库获取成员列表 */;
    vm.MemberAttendances = members.Select(m => new MemberAttendanceItem
    {
        MemberId = m.Id,
        MemberName = m.Name,
        IsPresent = false // 默认未勾选
    }).ToList();
    return View(vm);
}

内容的提问来源于stack exchange,提问作者Katakyie Kofi Poku

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 15:59:54