从同一表单同时保存多行-dotnet core [英] Save multiple rows simultaneously from the same form - dotnet core

查看:75
本文介绍了从同一表单同时保存多行-dotnet core的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a table that has one empty column into which user can enter a comment:

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行),并一键提交按钮将其全部保存。

There is a lot of data to comment on, I can't expect a user to open an 'Edit' page and type in a comment one by one. Instead I need user to be able to input a bunch of comments (say 20 at a time against 20 rows) and save them all at one click of submit button.

如果您想直接跳到可行的解决方案,请转到 EDIT#2 &看看Rudi接受的答案

If you want to jump straight to working solution go to EDIT #2 & look at Rudi's accepted answer

查看

        <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;组; }

I've learned today that I need to use List to be able to iterate through the lines in table by the use of @for loop (as shown above). before I was trying to use IEnumerable. So I added a definition to the model for 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; }

       [...]

    }

控制器:

现在这是我需要帮助的地方,我的代码根本不会引发任何错误。当我按提交时,什么也没发生:

Now this is where I need the help with, my code doesn't throw any errors at all. When I press Submit nothing happens:

        [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; }

        [...]
    }
}

更改为控制器:

现在这是我需要帮助的地方,我的代码在任何时候都不会引发任何错误

Now this is where I need the help with, my code doesn't throw any errors at all when at the current state - when I press Submit nothing happens and no data gets saved to the database.

但是,当您取消注释_context.Update(tbMapViewModel.TBMapBalances)行时,在当前状态-当我按Submit时,什么也不会发生,也没有数据保存到数据库。我收到一个错误,指出List不是任何模型的一部分,并且没有找到。

however, when you uncomment line _context.Update(tbMapViewModel.TBMapBalances); I get an error that List is not part of any Model and is not found.

此外,下面的代码是我写的,试图遵循以下SO帖子:update-multiple -记录一次在asp-net-mvc中-最初我试图使其异步,但是我遇到了更多错误,无法继续。我以为我会尽可能地遵循它,希望它能为我提供一个工作的起点。

Also, below code is something I wrote trying to follow this SO post: update-multiple-records-at-once-in-asp-net-mvc - Initially I was trying to make it Async but I was getting even more errors and couldn't continue. I thought I am going to follow it as closely as possible in hope that it will get me a working starting point.

        [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 ,并且由于不断出现的修补程序和附加内容而没有升级我的安装。

First of all if any of you guys are - like me - stuck using .net core 1.0.0 make sure you upgrade to the latest version first (1.1.7 lts). Part of my grief was that I was an USER 1.0 and did not upgrade my installation as fixes and additions kept coming out. Don't be that guy, like I was...

以下所有都是感谢Rudi的帮助:

All below is thanks to Rudi's help:

查看

@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操作将设置为当前位置。

Put your "Method", "Controller" in (Html.BeginForm("TbMapViewEdit", "TbMap")) otherwise the form POST action will be set to the current location.

模型

为简洁起见,被截断。我有一个带有List的视图模型,该模型将把数据保存到其中,而另一个表仅用于显示一些信息。

Truncated for brevity. I have view model with List that I will be saving the data to and one other table just for displaying some info.

 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)。

Error that I was making here is that I was trying to replace the key with essentially the copy of itself (Find ID of 1 and set it to ID of 1) instead of picking up on the actual one field that I needed to edit which in my case was UniqueAdp.

然后出现了新的东西,那就是编辑器模板:

Then came the new thing to me, which was Editor Template:

编辑器模板

在视图文件夹下的共享文件夹中创建一个名为 EditorTemplates 的文件夹,其中包含您要编辑的模型的确切名称。在我的情况下,该模型称为 TBMapBalances ,因此我在新创建的文件夹内创建了 TBMapBalances.cshtml 文件,然后将其粘贴这(最初在我的主视图文件中):

Create a folder called EditorTemplates in 'Shared' Folder under your 'Views' folder with the exact name of the model that you intend to edit. In my case the model was called TBMapBalances so I created a TBMapBalances.cshtml file inside the newly created folder, then pasted this (this was originally in my main view file):

@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 core中不起作用,并留在那儿只是提醒自己我需要以某种方式进行此操作。

By the way the new { @class = "control-label_DI" } is there to supposedly add class to each created input field. This doesn't seem to work in .net core and is left there just as a reminder to myself that I need to do this somehow.

研究:

更新多个记录一次在asp.net mvc中

https://docs.microsoft.com/zh-CN/aspnet/core/mvc/views/working-with-forms

http: //www.binaryintellect.net/articles/b1e0b153-47f4-4b29-8583-958aa22d9284.aspx

如何在MVC Core中绑定阵列

https://www.red-gate.com/simple-talk/dotnet/asp-net/model-binding-asp-net-core/

ASP.NET Core 1.0 POST IEnumerable< &到控制器

推荐答案

此问题包括两个部分,首先是需要一种方法编辑数据集合。可以使用EditorTemplates来解决,该方法包括创建单个编辑器模型,然后在要编辑的项目集合上调用 @ Html.EditorFor(..)

This problem has 2 parts to it, the first is that there needed to be a way to edit collections of data. This can be solved with EditorTemplates, which involves creating a single editor model and then calling @Html.EditorFor(..) on the collection of items you wish to edit.

提供了一个最小样本(完整Fx,不是Core)在Github上

A minimal sample (Full Fx, not Core) is available on Github.

第二个问题是实体的更新方式,更改的属性是错误的,因此无法保存(将PK更新为PK),并且已跟踪该实体时附加该实体。

The second problem was with the way the entities were being updated, the property being changed was wrong and hence not saving (the PK was being updated to the PK) and the entity was being attached when it's already tracked.

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为您做什么数据库。它会被上下文自动跟踪,因此它已经连接并准备更新,您所做的任何更改都将被自动跟踪并随后保存。

It's important to remember what Entity Framework does for you when you retrieve a model from the database. It is automatically tracked by the context, and so it's already attached and ready to update, anything you change will be automatically tracked and subsequently saved.

对<$ c的调用$ c> _context.Update(..)尝试将基于ID的未跟踪模型(来自POST数据)附加到上下文,但是因为您已经将该ID拔出(输入您的 .Where(..)。FirstOrDefault(..))已被跟踪,因此会炸毁。

The call to _context.Update(..) tries to attach the non-tracked models (from your POSTed data) to the context based on ID, but because you've already pulled that ID out (in your .Where(..).FirstOrDefault(..)) it's already tracked, and so blows up.

此外,鉴于这是EFC 1.0,并且您没有使用 .SingleOrDefault(..) .Find(..)方法$ c>可能是在主键字段上使用的更好方法。

Also given that this is EFC 1.0 and you have no .Find(..) method, using .SingleOrDefault(..) is probably a better method to use on a primary key field.

您重写的代码可能是这样的:

Your rewritten code could be as so:

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 循环:

For posterity though I wouldn't recommend it for security reasons, if you wanted to attach your whole posted model to the context (based on ID) and update it, you can do so with code similar to your original, removing the foreach loop:

_context.UpdateRange(tbMapViewModel.TBMapBalances);
await _context.SaveChangesAsync();

(我不建议这样做,因为发布的所有内容都将在数据库中设置,从最好只根据第一个代码集设置要更新的字段,但是应该比foreach循环要快,因为您不需要从数据库中加载并保存回去,只有后者)

(I don't recommend it because everything that was posted will then be set in the database, from experience it's advisable to only set the fields you're expecting to update as per the first code set. It should, however, be quicker than the foreach loop given that you're not loading from the database and saving back in, only the latter)

这篇关于从同一表单同时保存多行-dotnet core的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆