代码之家  ›  专栏  ›  技术社区  ›  PawelKosi

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

  •  4
  • PawelKosi  · 技术社区  · 7 年前

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

    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>
    

    模型

    今天我了解到,我需要使用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; }
    
           [...]
    
        }
    

    控制器:

    这就是我需要帮助的地方,我的代码根本不会抛出任何错误。当我按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");
            }
    

    编辑#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; }
    
            [...]
        }
    }
    

    控制器更改:

    现在,这就是我需要帮助的地方,我的代码在当前状态下不会抛出任何错误-当我按Submit时,不会发生任何事情,也不会将任何数据保存到数据库中。

    但是,当您取消注释行\u上下文时。更新(tbMapViewModel.TBMapBalances);我得到一个错误,列表不是任何模型的一部分,也找不到。

    此外,下面的代码是我在asp net mvc中一次更新多个记录的文章:最初我试图使其异步,但遇到更多错误,无法继续。我想我会尽可能地关注它,希望它能给我一个工作的起点。

            [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)。我悲痛的一部分是 我是用户1.0 并且没有升级我的安装,因为修复和添加的内容不断出现。别像我那样。。。

    以下都要感谢鲁迪的帮助:

    看法

    @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")) 否则,form POST操作将设置为当前位置。

    模型

    为简洁起见,已截断。我有一个视图模型和一个列表,我将把数据保存到其中,另一个表只是为了显示一些信息。

     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 回复  |  直到 7 年前
        1
  •  1
  •   Rudi Visser    7 年前

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

    提供最小样本(完整Fx,非核心) 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;
    }
    

    当您从数据库检索模型时,记住实体框架为您做了什么很重要。它由上下文自动跟踪,因此它已经附加并准备好更新,您所更改的任何内容都将自动跟踪并随后保存。

    呼叫 _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循环,则应该比foreach循环快,只保存后者)

        2
  •  0
  •   Max    7 年前

    您是否已经在razor页面中输入了注释?我没有看到他们。您要做的是创建一个表单,其中包含您希望循环中每个项目的输入类型。然后,每个表单将迭代器作为隐藏值引用,以便在发布时传递。如果循环是 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