【问题标题】:Updating Master-Detail / Parent-Child data in MVC在 MVC 中更新 Master-Detail / Parent-Child 数据
【发布时间】:2018-04-27 18:44:59
【问题描述】:

拥有一个带有主从样式视图的发票视图。主是发票,明细是发票行。我正在尝试获取详细数据项以保存在编辑帖子上,但是在到达控制器上的帖子编辑时详细数据丢失了。所以主数据保存得很好,但细节显然没有保存。

发票类:

public class Invoice
{
    public Invoice()
    {

    }

    [Required]
    [Key]
    public int InvoiceID { get; set; }

    [Required]
    [StringLength(30)]
    [DisplayName("Invoice Number")]
    public string InvoiceNumber { get; set; }

    [Required, DatabaseGenerated(DatabaseGeneratedOption.Computed)]
    [DataType(DataType.Date)]
    [Column(TypeName = "Date")]

    [DisplayName("Invoice Date")]
    public DateTime InvoiceDate { get; set; }

    public List<InvoiceLine> InvoiceLines { get; set; }

    [ForeignKey("Client")]
    public int OwnerClientIDFK { get; set; }

    [DisplayName("Client")]
    public Client Client { get; set; }
}

发票行类:

public class InvoiceLine
{
    public InvoiceLine()
    {

    }

    [Key]
    [Required]
    public int InvoiceLineId { get; set; }

    [Required]
    [StringLength(255)]
    [DisplayName("Item")]
    public string ItemName { get; set; }

    [DisplayName("Description")]
    public string ItemDescription { get; set; }

    [Required]
    public int Quantity { get; set; }

    [Required]
    [DisplayFormat(DataFormatString = "{0:C}", ApplyFormatInEditMode = true)]
    public decimal Value { get; set; }

    [ForeignKey("ParentInvoice")]
    public int InvoiceID { get; set; }

    public Invoice ParentInvoice { get; set; }

} 

控制器编辑(获取):

    public ActionResult Edit(int? id)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }
        // Invoice invoice = db.Invoices.Find(id);
        Invoice invoice = db.Invoices.Include(i => i.InvoiceLines)
                            .Include(i => i.Client)
                            .Where(c => c.InvoiceID == id).FirstOrDefault();

        if (invoice == null)
        {
            return HttpNotFound();
        }
        ViewBag.OwnerClientIDFK = new SelectList(db.Clients, "ClientId", "CompanyName", invoice.OwnerClientIDFK);
        return View(invoice);
    } 

控制器编辑(帖子):

    public ActionResult Edit([Bind(Include = "InvoiceID,InvoiceNumber,InvoiceDate,OwnerClientIDFK")] Invoice invoice)
    {
        if (ModelState.IsValid)
        {
            db.Entry(invoice).State = EntityState.Modified;

            foreach (var invLine in invoice.InvoiceLines)
            {
                db.Entry(invLine).State = EntityState.Modified;
            }

            db.SaveChanges();
            return RedirectToAction("Index");
        }
        ViewBag.OwnerClientIDFK = new SelectList(db.Clients, "ClientId", "CompanyName", invoice.OwnerClientIDFK);
        return View(invoice);
    } 

所以在上面,当它到达foreach时,它会抛出一个异常,因为InvoiceLines是null。

查看:

@model DemoApp.Entities.Invoice

@{
    ViewBag.Title = "Edit";
}

<h2>Edit</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()

<div class="form-horizontal">
    <h4>Invoice</h4>
    <hr />
    @Html.ValidationSummary(true, "", new { @class = "text-danger" })
    @Html.HiddenFor(model => model.InvoiceID)

    <div class="form-group">
        @Html.LabelFor(model => model.InvoiceNumber, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.InvoiceNumber, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.InvoiceNumber, "", new { @class = "text-danger" })
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(model => model.InvoiceDate, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.InvoiceDate, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.InvoiceDate, "", new { @class = "text-danger" })
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(model => model.OwnerClientIDFK, "OwnerClientIDFK", htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.DropDownList("OwnerClientIDFK", null, htmlAttributes: new { @class = "form-control" })
            @Html.ValidationMessageFor(model => model.OwnerClientIDFK, "", new { @class = "text-danger" })
        </div>
    </div>

    <div class="form-group">
        <h2>Invoice Lines</h2>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })

        <div class="row">
            <div class="col-md-8">
                <table class="table">
                    <thead>
                        <tr>
                            <th>Item</th>
                            <th>Description</th>
                            <th>Qty</th>
                            <th>Unit Value</th>
                        </tr>
                    </thead>
                    <tbody>
                        @for (int i = 0; i < Model.InvoiceLines.Count; i++)
                        {
                            <tr>
                                <td>@Html.EditorFor(x => x.InvoiceLines[i].ItemName, new { htmlAttributes = new { @class = "form-control" } })</td>
                            </tr>
                        }
                    </tbody>

                </table>
            </div>
        </div>

    </div>

    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" value="Save" class="btn btn-default" />
        </div>
    </div>

</div>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

如何获取它来更新详细信息/子数据?

任何帮助表示赞赏。提前致谢。

【问题讨论】:

    标签: asp.net post model-view-controller edit master-detail


    【解决方案1】:

    您没有在编辑后操作方法上绑定 InvoiceLines

    public ActionResult Edit([Bind(Include = "InvoiceID,InvoiceNumber,InvoiceDate,OwnerClientIDFK,InvoiceLines")] 发票发票) {

    【讨论】:

    • 是的,我试过了。除了 InvoiceLines 之外,创建的绑定是相同的,但它也不起作用。 :(
    • 我在 InvoiceLines 中的所有字段的视图中添加了 hiddenfor 条目,除了返回父发票的对象引用,现在它似乎可以工作了。如果表单上没有人使用原始绑定的数据并不会自动将其路由回帖子,这似乎很奇怪。
    【解决方案2】:

    您可能需要在获得父实体时加载发票行。

    db.Entry(invoice).Collection(i=>i.InvoiceLines).Load() 应该这样做。

    【讨论】:

    • 你的意思是在控制器的get Edit方法中吗? ...发票行使用 Include 在 get Edit 方法中加载 - 发票行数据存在于视图中进行编辑,但不会返回到控制器上的 post Edit 方法.. 发票数据返回但没有发票行。使用 Include 和上面的有区别吗?
    【解决方案3】:

    首先,这是一个棘手的场景。有很多活动部件。我对此非常感兴趣并编写了一个示例应用程序。 :)

    这里是 GitHub 上的 repo 的链接: https://github.com/benday/asp-mvc-invoice-sample

    有一些事情正在合谋导致您的代码出现问题。

    1) 具有讽刺意味的是,您在 Edit(Invoice model) 方法中拥有的 Bind[] 正在妨碍您。如果您完全删除它,ASP MVC 将尝试绑定所有内容。现在,它只绑定您告诉它绑定的内容,并且由于您不包含 InvoiceLines 集合,因此它会变为 null。

    2) 如果您希望 ASP MVC 尝试将数据绑定到您的模型中,您需要将该数据 POST 回服务器。如果这些发票行不是 html 表单并且没有表示为表单字段,那么这些数据就会丢失。

    您必须绘制发票行的代码缺少填充 InvoiceLine 对象所需的大部分字段。

    @Html.EditorFor(x => x.InvoiceLines[i].ItemName, new { htmlAttributes = new { @class= "form-control" } })

    Editor Template for the InvoiceLine class

    在我的示例代码中,我为 InvoiceLine 类创建了一个编辑器模板。这使我可以通过调用@Html.EditorFor(model =&gt; model.Invoice.InvoiceLines) asdf 轻松为 InvoiceLines 创建可绑定的 html

    我知道这不是最简单的答案,但我希望这可以帮助您克服困难。

    【讨论】:

    • 非常感谢您的回复和项目。我将在下周详细讨论。实际上,在我有机会通过添加填充 InvoiceLines 对象所需的未使用/未显示的数据字段作为视图上的隐藏字段来查看此问题之前,我实际上解决了这个问题。之后绑定工作正常。当我下周有更多时间时,我需要仔细看看你发送的项目。你包括一个数据库吗?再次感谢。
    • 是的。数据库在那里。嗯...实体框架迁移在那里,因此您可以使用“dotnet ef 数据库更新”创建数据库。
    猜你喜欢
    • 2013-01-07
    • 2018-03-12
    • 2016-05-12
    • 2012-10-31
    • 2021-09-25
    • 2012-05-27
    • 1970-01-01
    • 1970-01-01
    • 2021-11-11
    相关资源
    最近更新 更多