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

EF Core批量更新任务数据:模型绑定IList为空的问题与解决

EF Core任务复选框变更绑定与保存问题

问题描述

我开发了一个EF Core应用,用于创建新员工并为其分配多项任务。员工完成任务后,需打开列有所有任务的表单,勾选对应任务的复选框后提交表单。

我了解如何跟踪单个实体的变更,但将任务加载到IList<TaskAssignment>后,始终无法通过模型绑定跟踪变更——Post方法中的IList始终为空,也找不到可行的方法将变更保存到数据库表中。尝试了网上多种方法,导致代码变得混乱。

原模型代码

public class TasksOutstandingModel : DI_BasePageModel
{
    [BindProperty]
    public IList<TaskAssignment>? Tasks { get; set; }    

    public async Task<IActionResult> OnGetAsync(int? id)
    {
        var tasks = _tasksContext.TaskAssignment.Where(t => t.EmpReferenceID == id);

        Tasks = await tasks.ToListAsync();

        if (tasks == null)
        {
            return NotFound(id);
        }
        return Page();
    }

    public async Task<IActionResult> OnPostAsync(uint? id, IList<TaskAssignment> tasksList)
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        var originalEntity = await _tasksContext.TaskAssignment.AsNoTracking().FirstOrDefaultAsync(me => me.EmpReferenceID == id && me.HRID == 1);
        var changed = Request.Form["hr+1"];
        bool changedContrSent = originalEntity.Done != Convert.ToBoolean(changed);
        if (changedContrSent)
        {
            //do stuff
        }
        foreach (var task in tasksList)
        {
            TaskAssignment taskAssignment = _tasksContext.TaskAssignment.Find(task.TaskAssignmentID);
            taskAssignment.Done = task.Done;
            _tasksContext.Entry(taskAssignment).State = EntityState.Modified;
            Console.WriteLine(taskAssignment.TaskAssignmentID);
        }

        try
        {
            await _tasksContext.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!TasksExits((int)Tasks.FirstOrDefault().EmpReferenceID))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }
        return RedirectToPage("./TasksOutstanding", Tasks.FirstOrDefault().EmpReferenceID);
    }
}

原视图代码(部分)

<form method="post">
    @for (int i = 0; i < Model.Tasks.Count(); i++)
    {
        <input type="hidden" asp-for="Tasks[i].TaskAssignmentID"/>
        <input type="hidden" asp-for="Tasks[i].EmpReferenceID"/>
        <input type="hidden" asp-for="Tasks[i].AssistantID"/>
        <input type="hidden" asp-for="Tasks[i].EduID"/>
        <input type="hidden" asp-for="Tasks[i].FKID"/>
        <input type="hidden" asp-for="Tasks[i].HRID"/>
        <input type="hidden" asp-for="Tasks[i].PayrollID"/>
        <input type="hidden" asp-for="Tasks[i].DueDateInD"/>
        <input type="hidden" asp-for="Tasks[i].Dependency"/>
        <input type="hidden" asp-for="Tasks[i].Done"/>
        <input type="hidden" asp-for="Tasks[i].ITId"/>
    }
<div class="correctWidth">
            <button type="button" class="collapsibleHeading">Aufgaben für Bildungsmanagement</button>
            <div class="collapsing-content">
                @{
                    var allEdu = Model.Tasks.Where(t => t.EduID != null);
                    foreach (var item in allEdu)
                    {
                        <div class="form-group form-check">
                            @* if the item is already marked as done then we disable the checkbox so it can't be unchecked *@
                            @if (item.Done || disable)
                            {
                                <input class="form-check-input" asp-for="@item.Done" name="edu+@item.EduID" disabled/>
                                disable = false;
                            }
                            else
                            {
                                <input class="form-check-input" asp-for="@item.Done" name="edu+@item.EduID" />
                            }
  
                            @if (shortdesc != "")
                            {
                                <button type="button" class="collapsible">
                                    <div class="summaryText">@summary</div>
                                    @if (!duedate.Equals(01 / 01 / 0001))
                                    {
                                        <div class="dueDateDiv">@duedate</div>
                                    }
                                    else
                                    {
                                        <div class="dueDateDiv"></div>
                                    }
                                    <div class="SymbolDiv"></div>
                                </button>
                                <div class="collapsing-content">
                                    @Html.Raw(shortdesc)
                                </div>
                            }
                            else
                            {
                                <div class="notCollapsible">
                                    <div class="summaryText">@summary</div>
                                </div>
                            }
                        </div>
                    }
                }
            </div>
        </div>
<div class="form-group">
    <input type="hidden" asp-for="Tasks.First().EmpReferenceID"/>
    <input type="submit" value="Speichern" class="btn btn-primary"/>
</div>
</form>

解决方法

编辑:

感谢Jason Pan的建议,代码现已正常运行。在视图中移除了不必要的for循环;在控制器中添加了UpdateTasks()方法,通过处理Request.Form获取复选框的变更值,更新对应实体并标记为修改状态,最后在OnPostAsync中调用该方法并保存变更到数据库:

UpdateTasks方法代码

private void UpdateTasks(uint? ident)
{
    string ss = "";
    byte key = 0;
    bool donezoes = false;
    var tasks = _tasksContext.TaskAssignment.Where(t => t.EmpReferenceID == ident);
    Tasks = tasks.ToList();
    var dict = Request.Form.ToDictionary(d => d.Key, d => d.Value);
    foreach (var item in dict)
    {
        //获取所有变更的变量
        //Key是HTML元素的名称
        //Value是复选框的值,即true或false
        if (item.Key.Contains("EmpReferenceID")) break;
        if (item.Key.Contains("edu"))
        {
            //复选框命名为"edu+x",x是需要从数据库中获取的对象ID
            ss = item.Key.Substring(4);
            key = Convert.ToByte(ss);
            //将值转换为布尔类型
            donezoes = Convert.ToBoolean(item.Value);
            TaskAssignment ta = Tasks.FirstOrDefault(x => x.EduID == key);
            //更新数据库实体的Done状态
            ta.Done = donezoes;
            //标记实体为已修改
            _tasksContext.Entry(ta).State = EntityState.Modified;
        }
    //其他类型任务的处理逻辑类似
    }
}

修改后的OnPostAsync方法

public async Task<IActionResult> OnPostAsync(uint? id)
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    UpdateTasks(id);
    ...
    
    try
    {
        await _tasksContext.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!TasksExits((int)Tasks.FirstOrDefault().EmpReferenceID))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }
    return RedirectToPage("./TasksOutstanding", Tasks.FirstOrDefault().EmpReferenceID);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 15:31:58