C# 从同一表单同时保存多行-dotnet core
我有一个表,其中有一个空列,用户可以在其中输入注释:C# 从同一表单同时保存多行-dotnet core,c#,asp.net-core,.net-core,asp.net-core-mvc,C#,Asp.net Core,.net Core,Asp.net Core Mvc,我有一个表,其中有一个空列,用户可以在其中输入注释: Table ----- TbMapId | UniqueAdp | Dealership | Line -------------------------------------------------------------------- 1 | [Insert comment here] | Derby | abc123 2 |
Table
-----
TbMapId | UniqueAdp | Dealership | Line
--------------------------------------------------------------------
1 | [Insert comment here] | Derby | abc123
2 | [Insert comment here] | Keighley | cda345
3 | [Insert comment here] | Manchester | 876ghj
有很多数据需要评论,我不能期望用户打开“编辑”页面并逐个输入评论。相反,我需要用户能够输入一堆评论(比如20行一次输入20条),并在点击提交按钮时保存所有评论
如果您想直接跳转到工作解决方案,请转到“编辑2”&查看鲁迪接受的答案
查看
<form asp-action="TbMapViewEdit">
<div class="col-lg-6">
<div class="row">
<input type="submit" value="Save" class="btn btn-primary" />
<div class="col-md-12">
<table class="table table-condensed table-bordered table-hover">
<thead>
<tr>
<td><b>TEMP ID</b></td>
<td><b>Map To</b></td>
<td><b>Accounts Code</b></td>
<td><b>Line</b></td>
<td><b>Map Result</b></td>
</tr>
</thead>
<tbody>
@for (int i = 0; i < Model.TBMapBalancesList.Count; i++)
{
<tr>
<td>
@Html.DisplayFor(Model => Model.TBMapBalancesList[i].TbMapId)
@Html.HiddenFor(Model => Model.TBMapBalancesList[i].TbMapId)
</td>
<td>@Html.EditorFor(Model => Model.TBMapBalancesList[i].UniqueAdp, new { @class = "control-label_DI" })</td>
<td>@Html.DisplayFor(Model => Model.TBMapBalancesList[i].AccountsCode)</td>
<td>@Html.DisplayFor(Model => Model.TBMapBalancesList[i].Line)</td>
<td>@Html.DisplayFor(Model => Model.TBMapBalancesList[i].MapResult)</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
</form>
<form asp-action="TbMapViewEdit">
<div class="col-lg-6">
<div class="row">
<input type="submit" value="Save" class="btn btn-primary" />
<div class="col-md-12">
<table class="table table-condensed table-bordered table-hover">
<thead>
<tr>
<td><b>TEMP ID</b></td>
<td><b>Map To</b></td>
<td><b>Accounts Code</b></td>
<td><b>Line</b></td>
<td><b>Map Result</b></td>
</tr>
</thead>
<tbody>
@for (int i = 0; i < Model.TBMapBalances.Count; i++)
{
<tr>
<td>
@Html.DisplayFor(Model => Model.TBMapBalances[i].TbMapId)
@Html.HiddenFor(Model => Model.TBMapBalances[i].TbMapId)
</td>
<td>@Html.EditorFor(Model => Model.TBMapBalances[i].UniqueAdp, new { @class = "control-label_DI" })</td>
<td>@Html.DisplayFor(Model => Model.TBMapBalances[i].AccountsCode)</td>
<td>@Html.DisplayFor(Model => Model.TBMapBalances[i].Line)</td>
<td>@Html.DisplayFor(Model => Model.TBMapBalances[i].MapResult)</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
</form>
@using (Html.BeginForm("TbMapViewEdit", "TbMap"))
{
<div class="col-lg-6">
<div class="row">
<input type="submit" value="Save" class="btn btn-primary" />
<div class="col-md-12">
<table class="table table-condensed table-bordered table-hover">
<thead>
<tr>
<td><b>TEMP ID</b></td>
<td><b>Map To</b></td>
<td><b>Accounts Code</b></td>
<td><b>Line</b></td>
<td><b>Map Result</b></td>
</tr>
</thead>
<tbody>
@Html.EditorFor(m => m.TBMapBalances);
</tbody>
</table>
</div>
</div>
</div>
}
控制器:
这就是我需要帮助的地方,我的代码根本不会抛出任何错误。当我按Submit时,什么也没有发生:
[Authorize]
[HttpPost]
public async Task<IActionResult> TbMapViewEdit(TbMapViewModel tbMapViewModel)
{
if (ModelState.IsValid)
{
foreach (var TbListId in tbMapViewModel.TBMapBalancesList)
{
var getCode = _context.TBMapBalances.Where(p => p.TbMapId == TbListId.TbMapId).FirstOrDefault();
if (getCode != null)
{
getCode.TbMapId = TbListId.TbMapId;
}
}
try
{
_context.Update(tbMapViewModel.TBMapBalances);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
throw;
}
}
return RedirectToAction("TbMapView");
}
编辑#2-拯救英雄-非常感谢@rudiviser的帮助
首先,如果你们中的任何一个像我一样被困在使用.NETCore1.0.0中
确保首先升级到最新版本(1.1.7 lts)。我的部分悲伤是因为我是一名用户1.0,没有升级我的安装,因为修复程序和附加程序不断出现。别做那个人,就像我
以下一切都要感谢鲁迪的帮助:
查看
<form asp-action="TbMapViewEdit">
<div class="col-lg-6">
<div class="row">
<input type="submit" value="Save" class="btn btn-primary" />
<div class="col-md-12">
<table class="table table-condensed table-bordered table-hover">
<thead>
<tr>
<td><b>TEMP ID</b></td>
<td><b>Map To</b></td>
<td><b>Accounts Code</b></td>
<td><b>Line</b></td>
<td><b>Map Result</b></td>
</tr>
</thead>
<tbody>
@for (int i = 0; i < Model.TBMapBalancesList.Count; i++)
{
<tr>
<td>
@Html.DisplayFor(Model => Model.TBMapBalancesList[i].TbMapId)
@Html.HiddenFor(Model => Model.TBMapBalancesList[i].TbMapId)
</td>
<td>@Html.EditorFor(Model => Model.TBMapBalancesList[i].UniqueAdp, new { @class = "control-label_DI" })</td>
<td>@Html.DisplayFor(Model => Model.TBMapBalancesList[i].AccountsCode)</td>
<td>@Html.DisplayFor(Model => Model.TBMapBalancesList[i].Line)</td>
<td>@Html.DisplayFor(Model => Model.TBMapBalancesList[i].MapResult)</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
</form>
<form asp-action="TbMapViewEdit">
<div class="col-lg-6">
<div class="row">
<input type="submit" value="Save" class="btn btn-primary" />
<div class="col-md-12">
<table class="table table-condensed table-bordered table-hover">
<thead>
<tr>
<td><b>TEMP ID</b></td>
<td><b>Map To</b></td>
<td><b>Accounts Code</b></td>
<td><b>Line</b></td>
<td><b>Map Result</b></td>
</tr>
</thead>
<tbody>
@for (int i = 0; i < Model.TBMapBalances.Count; i++)
{
<tr>
<td>
@Html.DisplayFor(Model => Model.TBMapBalances[i].TbMapId)
@Html.HiddenFor(Model => Model.TBMapBalances[i].TbMapId)
</td>
<td>@Html.EditorFor(Model => Model.TBMapBalances[i].UniqueAdp, new { @class = "control-label_DI" })</td>
<td>@Html.DisplayFor(Model => Model.TBMapBalances[i].AccountsCode)</td>
<td>@Html.DisplayFor(Model => Model.TBMapBalances[i].Line)</td>
<td>@Html.DisplayFor(Model => Model.TBMapBalances[i].MapResult)</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
</form>
@using (Html.BeginForm("TbMapViewEdit", "TbMap"))
{
<div class="col-lg-6">
<div class="row">
<input type="submit" value="Save" class="btn btn-primary" />
<div class="col-md-12">
<table class="table table-condensed table-bordered table-hover">
<thead>
<tr>
<td><b>TEMP ID</b></td>
<td><b>Map To</b></td>
<td><b>Accounts Code</b></td>
<td><b>Line</b></td>
<td><b>Map Result</b></td>
</tr>
</thead>
<tbody>
@Html.EditorFor(m => m.TBMapBalances);
</tbody>
</table>
</div>
</div>
</div>
}
控制器
[Authorize]
[HttpPost]
public IActionResult TbMapViewEdit(TbMapViewModel tbMapViewModel)
{
if (ModelState.IsValid)
{
foreach (var TbListId in tbMapViewModel.TBMapBalances)
{
var getCode = _context.TBMapBalances.Where(p => p.TbMapId == TbListId.TbMapId).FirstOrDefault();
if (getCode != null)
{
getCode.UniqueAdp = TbListId.UniqueAdp;
}
}
_context.SaveChanges();
}
return RedirectToAction("TbMapView");
}
我在这里犯的错误是,我试图用密钥本身的副本(查找ID为1并将其设置为ID为1)替换密钥,而不是选择我需要编辑的实际字段,在我的例子中,该字段是UniqueAdp
然后我想到了一个新东西,那就是编辑器模板:
编辑器模板
在“视图”文件夹下的“共享”文件夹中创建一个名为EditorTemplates
的文件夹,其中包含要编辑的模型的确切名称。在我的例子中,该模型被称为TBMapBalances
,因此我在新创建的文件夹中创建了一个TBMapBalances.cshtml
文件,然后粘贴了它(这最初是在我的主视图文件中):
@model ASPNET\u Core\u 1\u 0.Models.tbmap
@DisplayFor(Model=>Model.TbMapId)
@Html.HiddenFor(Model=>Model.TbMapId)
@EditorFor(Model=>Model.UniqueAdp,新的{@class=“control-label_DI”})
@DisplayFor(Model=>Model.AccountsCode)
@DisplayFor(Model=>Model.Line)
@DisplayFor(Model=>Model.MapResult)
顺便说一句,新的{@class=“control-label_DI”}
应该是向每个创建的输入字段添加类的。这在.NETCore中似乎不起作用,留在那里只是为了提醒自己,我需要以某种方式这样做
研究:
您是否已经在razor页面中输入了注释?我没有看到他们。您要做的是创建一个表单,其中包含您希望循环中每个项目的输入类型。然后,每个表单将迭代器作为隐藏值引用,以便在发布时传递。如果循环是
foreach(Model.items中的var-item){}
,那么您将拥有一个表单,其中包含该块中的输入,并带有一个类似
的隐藏输入,这将允许您使用所需的任何标识符发布,以将特定表单数据附加到正确的模型
有关循环中表单的结构,请参见此答案。此问题有两个部分,第一部分是需要有一种方法来编辑数据集合。这可以通过EditorTemplates来解决,它涉及到创建一个编辑器模型,然后对要编辑的项集合调用
@Html.EditorFor(..)
最低限度的样本(完整的外汇,而不是核心)是可用的
第二个问题是实体更新的方式,更改的属性错误,因此没有保存(PK被更新为PK),并且实体在被跟踪时被附加
foreach (var TbListId in tbMapViewModel.TBMapBalancesList)
{
var getCode = _context.TBMapBalances.Where(p => p.TbMapId == TbListId.TbMapId).FirstOrDefault();
if (getCode != null)
{
getCode.TbMapId = TbListId.TbMapId;
}
}
try
{
_context.Update(tbMapViewModel.TBMapBalances);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
throw;
}
当您从数据库检索模型时,记住实体框架为您做了什么是很重要的。它由上下文自动跟踪,因此它已附加并准备更新,您所更改的任何内容都将自动跟踪并随后保存
调用\u context.Update(..)
尝试根据ID将未跟踪的模型(从发布的数据)附加到上下文,但因为您已经拉出了该ID(在.Where(..).FirstOrDefault(..)
中),它已经被跟踪,因此爆炸了
另外,考虑到这是EFC 1.0,并且您没有.Find(..)
方法,在主键字段上使用.SingleOrDefault(..)
可能是更好的方法
您重写的代码可以是这样的:
foreach (var postedModel in tbMapViewModel.TBMapBalancesList)
{
var dbModel = _context.TBMapBalances.SingleOrDefault(p => p.TbMapId == postedModel.TbMapId);
if (dbModel != null)
{
dbModel.UniqueAdp = postedModel.UniqueAdp;
}
}
await _context.SaveChangesAsync();
尽管出于安全原因,我不推荐给后代,但如果您想将整个发布模型附加到上下文(基于ID)并进行更新,可以使用与原始模型类似的代码,删除foreach
循环:
_context.UpdateRange(tbMapViewModel.TBMapBalances);
await _context.SaveChangesAsync();
(我不建议这样做,因为发布的所有内容都将在数据库中进行设置,根据经验,建议只设置您希望根据第一个代码集更新的字段。但是,如果您不是从数据库加载并保存,则应该比foreach循环快,只保存后者)@Html.EditorFor(Model=>Model.TBMapBalancesList[i].UniqueAdp,新的{@class=“control-label\u DI”})
这一行。HTML助手为数据库中的每个项目创建输入。您提到的逻辑已经存在,但是如果您阅读我的研究中的帖子,您会发现我到目前为止所做的似乎是正确的,我缺少的是controller中的POST action方法,或者更确切地说是如何让它再次将数据保存到TableThank。在我的编辑2中,我提到需要将核心框架和ef升级到最新版本。1.1.x中有“Find”。
foreach (var postedModel in tbMapViewModel.TBMapBalancesList)
{
var dbModel = _context.TBMapBalances.SingleOrDefault(p => p.TbMapId == postedModel.TbMapId);
if (dbModel != null)
{
dbModel.UniqueAdp = postedModel.UniqueAdp;
}
}
await _context.SaveChangesAsync();
_context.UpdateRange(tbMapViewModel.TBMapBalances);
await _context.SaveChangesAsync();