我有一个表,其中有一个空列,用户可以在其中输入注释:
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 &看看Rudi接受的答案
查看
<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>
模型
我今天已经了解到我需要使用List能够通过使用@for
循环遍历表中的行(如上所示)。在我尝试使用IEnumerable之前。所以我为public List<TBMapBalances> TBMapBalancesList { get; set; }
public class TbMapViewModel
{
public IEnumerable<ASPNET_Core_1_0.Models.TBMapBalances> TBMapBalances { get; set; }
public IEnumerable<ASPNET_Core_1_0.Models.TBMapUniqueADP> TBMapUniqueADP { get; set; }
public List<TBMapBalances> TBMapBalancesList { get; set; }
[...]
}
控制器:
现在这是我需要帮助的地方,我的代码根本不会抛出任何错误。当我按提交时,没有任何事情发生:
[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");
}
编辑#1
视图更改
<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 System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace ASPNET_Core_1_0.Models.TbMapViewModels
{
public class TbMapViewModel
{
public IEnumerable<ASPNET_Core_1_0.Models.TBMapUniqueADP> TBMapUniqueADP { get; set; }
public List<TBMapBalances> TBMapBalances { get; set; }
[...]
}
}
对控制器的更改:
现在这是我需要帮助的地方,我的代码在当前状态下根本不会抛出任何错误 - 当我按下提交时没有任何事情发生,没有数据保存到数据库。
然而,当你取消注释行_context.Update(tbMapViewModel.TBMapBalances);我得到一个错误,列表不是任何模型的一部分而且找不到。
此外,下面的代码是我写的东西试图遵循这个SO帖子:update-multiple-records-at-once-in-asp-net-mvc - 最初我试图让它成为Async但我得到的更多错误,无法继续。我想我会尽可能地密切关注它,希望它能让我有一个合适的起点。
[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.TbMapId = TbListId.TbMapId;
}
}
// _context.Update(tbMapViewModel.TBMapBalances);
_context.SaveChanges();
}
return RedirectToAction("TbMapView");
}
编辑#2 - 救援的英雄 - 非常感谢@RudiVisser寻求帮助
首先,如果你们中的任何人 - 像我一样 - 坚持使用.net core 1.0.0 确保首先升级到最新版本(1.1.7 lts)。我悲伤的部分原因是我是一个USER 1.0 并没有升级我的安装,因为修复和添加不断出现。不要那样的人,就像我一样......
以下全部归功于Rudi的帮助:
查看
@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>
}
将“方法”,“控制器”放在(Html.BeginForm("TbMapViewEdit", "TbMap"))
中,否则表单POST操作将设置为当前位置。
模型
为简洁而截断。我有一个带有List的模型,我将保存数据,另一个表用于显示一些信息。
public class TbMapViewModel
{
public IEnumerable<ASPNET_Core_1_0.Models.TBMapUniqueADP> TBMapUniqueADP { get; set; }
public List<TBMapBalances> TBMapBalances { get; set; } = new List<TBMapBalances>();
[...]
}
控制器
[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_Core_1_0.Models.TBMapBalances
<tr>
<td>
@Html.DisplayFor(Model => Model.TbMapId)
@Html.HiddenFor(Model => Model.TbMapId)
</td>
<td>@Html.EditorFor(Model => Model.UniqueAdp, new { @class = "control-label_DI" })</td>
<td>@Html.DisplayFor(Model => Model.AccountsCode)</td>
<td>@Html.DisplayFor(Model => Model.Line)</td>
<td>@Html.DisplayFor(Model => Model.MapResult)</td>
</tr>
顺便说一下,new { @class = "control-label_DI" }
应该为每个创建的输入字段添加类。这似乎不适用于.net核心,只是提醒我自己,我需要以某种方式这样做。
研究:
Update multiple records at once in asp.net mvc
https://docs.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms
http://www.binaryintellect.net/articles/b1e0b153-47f4-4b29-8583-958aa22d9284.aspx
How to bind an Array in MVC Core
https://www.red-gate.com/simple-talk/dotnet/asp-net/model-binding-asp-net-core/
答案 0 :(得分:1)
这个问题有两个部分,第一部分是需要一种编辑数据集合的方法。这可以通过EditorTemplates解决,它包括创建单个编辑器模型,然后在您要编辑的项目集合上调用@Html.EditorFor(..)
。
最小样本(Full Fx,而不是Core)可用on Github。
第二个问题是实体的更新方式,更改的属性是错误的,因此没有保存(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;
}
记住从数据库中检索模型时,Entity Framework为您做了什么很重要。它会被上下文自动跟踪,因此它已经附加并准备好更新,您更改的任何内容都将被自动跟踪并随后保存。
对_context.Update(..)
的调用尝试将非跟踪模型(从您的POST数据)附加到基于ID的上下文,但因为您已经将该ID删除(在{{1}中它已经被跟踪了,所以爆炸了。
另外,如果这是EFC 1.0并且您没有.Where(..).FirstOrDefault(..)
方法,则使用.Find(..)
可能是在主键字段上使用的更好方法。
您的重写代码可能如此:
.SingleOrDefault(..)
对于后代虽然出于安全原因我不推荐它,如果你想将整个发布的模型附加到上下文(基于ID)并更新它,你可以使用类似于原始的代码来实现,删除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();
循环:
foreach
(我不推荐它,因为发布的所有内容都将在数据库中设置,根据经验,建议只设置您希望按照第一个更新的字段但是,它应该比foreach循环更快,因为你没有从数据库加载并保存回来,只有后者)
答案 1 :(得分:0)
您是否已为剃须刀页面中已包含的评论提供输入?我没看见他们。你想要做的是创建一个表单,其中包含循环内循环中每个项目所需的输入类型。然后,每个表单都会引用迭代器作为隐藏值,以便在发布时传递。如果循环是foreach(var item in Model.items){}
,那么你将有一个包含该块中输入的表单,其隐藏输入看起来像<input type="hidden" name="index" value="@item.index"/>
这将允许您发布所需的任何标识符,以便将该特定表单数据附加到正确的模型。
请参阅此答案,了解循环Multiple forms on one MVC form, created with a loop, only the first submits data
中表单的结构