英文:
Update multiple rows in db table
问题
我有一个 EF Core 应用程序,在这个应用程序中,我正在创建新员工并分配多个任务给员工。当他们完成一个任务时,他们应该打开这个表单,其中列出了所有的任务,然后勾选相应的复选框并发送表单。
我知道如何跟踪单个实体并查看是否有任何更改,但是我正在加载一个 IList 中的任务,但我始终无法弄清楚如何使用模型绑定来跟踪任何更改。在 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)
{
// 做一些操作
}
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);
}
}
视图的一部分,有一些变量是没有意义的,我删除了其中获取这些变量值的部分。在顶部的变量处,我试图将 IList 传递给 Post 方法。但这种方式只有部分帮助,因为那些将是旧值,而不是更新后的值?
<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"/>
<!-- 其他隐藏的输入 -->
}
<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 (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" />
}
<!-- 其他部分 -->
</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() 方法:
private void UpdateTasks(uint? ident)
{
// 一些初始化的变量
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)
{
// 循环遍历获取到的变量
// 更多的条件判断,但是你明白了。
}
}
在我的 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);
}
英文:
I have an efcore application where I am creating new employees and assigning an employee multiple tasks. When they complete a task they are supposed to open this form where all the tasks are listed and check the corresponding box and send the form.
I know how to track a single entity and see if anything has changed but I'm loading my tasks in an IList and can't for the life of me figure out how to track any changes with Model Binding. In the Post method the IList is always empty and I can't find any working alternative ways to save the changes to my table.
I've been trying a couple of different approaches I found on the internet, so now my code is a bit of a mess.
The relevant parts of my model:
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);
}
part of my view, there are a few variables that don't make sense, I've cut the parts where I get the values for those. With the variables at the top I was attempting to pass the IList to the Post Method. Which is only semi-helpful anyways, since those would be the old values, not the updated ones?
<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>
Edit:
Thanks to Jason Pan's comment I have my code working now.
In my view I removed the for loop, that's unnecessary or I didn't know how to work with it(probably the latter)
In my controller I added the suggested UpdateTasks() method:
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)
{
//The dictionary gets all changed variables
//Key is the name of the html element
//value is the value, in this case of my checkboxes. So either true or false
if (item.Key.Contains("EmpReferenceID")) break;
if (item.Key.Contains("edu"))
{
//my checkboxes are named "edu+x", x being the ID of the object I need to pull from the db.
ss = item.Key.Substring(4);
key = Convert.ToByte(ss);
//assigning the value(true/false) to my boolean
donezoes = Convert.ToBoolean(item.Value);
TaskAssignment ta = Tasks.FirstOrDefault(x => x.EduID == key);
//assigning the boolean to the db entity
ta.Done = donezoes;
//marking the entity as modified
_tasksContext.Entry(ta).State = EntityState.Modified;
}
//more if conditions, but you get the point.
}
}
Calling the method in my OnPostAsync method and, most importantly, saving the changes to the db.
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);
}
答案1
得分: 0
你的代码如下所示:
public IList<TaskAssignment>? Tasks { get; set; }
在处理数据时,我看到 Tasks 没有被更新,这似乎是问题的根本原因。
public async Task<IActionResult> OnPostAsync(uint? id, IList<TaskAssignment> tasksList)
{
if (!ModelState.IsValid)
{
return Page();
}
...
if (changedContrSent)
{
// 进行操作
}
...
// --> 在下面添加以下代码,你可以创建一个自定义的 UpdateTask 方法来更新 Tasks
UpdateTask(Tasks);
try
{
await _tasksContext.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
...
}
return RedirectToPage("./TasksOutstanding", Tasks.FirstOrDefault().EmpReferenceID);
}
英文:
You code like below:
public IList<TaskAssignment>? Tasks { get; set; }
The Tasks, I saw that they are not updated when you process the data, which seems to be the root cause of the problem.
public async Task<IActionResult> OnPostAsync(uint? id, IList<TaskAssignment> tasksList)
{
if (!ModelState.IsValid)
{
return Page();
}
...
if (changedContrSent)
{
//do stuff
}
...
--> Add Below code, you can create custom UpdateTask method to update Tasks
UpdateTask(Tasks);
try
{
await _tasksContext.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
...
}
return RedirectToPage("./TasksOutstanding", Tasks.FirstOrDefault().EmpReferenceID);
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论