从同一表单同时保存多个行 - dotnet core

时间:2018-01-08 20:23:18

标签: c# asp.net-core .net-core asp.net-core-mvc

我有一个表,其中有一个空列,用户可以在其中输入注释:

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/

ASP.NET Core 1.0 POST IEnumerable<T> to controller

2 个答案:

答案 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

中表单的结构