简体   繁体   English

在MVC中更新主/从/父/子数据

[英]Updating Master-Detail / Parent-Child data in MVC

Have a view with a master-detail style view for an invoice. 具有发票的主从样式视图的视图。 Master is invoice, detail is invoice lines. 主数据是发票,明细数据是发票行。 I'm trying to get detail data items to save on upon an edit post but the detail data is lost upon reaching the post Edit on the controller. 我正在尝试获取明细数据项以保存在编辑帖子中,但是到达控制器上的“编辑”帖子时,详细数据会丢失。 So the master data saves fine but the detail is obviously not saved. 因此,主数据可以很好地保存,但细节显然没有保存。

Invoice Class: 发票类别:

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

Invoice Line class: 发票行类别:

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

} 

Controller Edit (get): 控制器编辑(获取):

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

Controller Edit (post): 控制器编辑(发布):

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

So in the above, when it reaches the foreach, it throws an exception because InvoiceLines is null. 因此,在上面,当到达foreach时,它会引发异常,因为InvoiceLines为null。

View: 视图:

@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")
}

How do I get it to update the detail / child data? 如何获取它来更新详细信息/子数据?

Any help appreciated. 任何帮助表示赞赏。 Thanks in advance. 提前致谢。

You are not binding InvoiceLines on edit post action method 您没有在编辑后操作方法上绑定InvoiceLines

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

What you probably need is to load the Invoice Lines when you get the parent entity. 您可能需要的是在获取父实体时加载发票行。

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

First off, this is kind of a tricky scenario. 首先,这是一个棘手的情况。 There are a lot of moving parts. 有很多活动部件。 I got really interested in this and wrote a sample application. 我对此非常感兴趣,并编写了一个示例应用程序。 :) :)

Here's the link to the repo on GitHub: https://github.com/benday/asp-mvc-invoice-sample 这是GitHub上仓库的链接: https : //github.com/benday/asp-mvc-invoice-sample

There are a handful of things going on that are conspiring to cause problems in your code. 正在进行中的少数事情正在合谋导致代码中的问题。

1) ironically, the Bind[] that you have in your Edit(Invoice model) method is getting in your way. 1)具有讽刺意味的是,您在Edit(Invoice model)方法中使用的Bind []妨碍了您。 If you remove it entirely, ASP MVC will attempt to bind everything. 如果将其完全删除,ASP MVC将尝试绑定所有内容。 Right now, it's only binding what you tell it to bind and since you don't include the InvoiceLines collection, then it's coming up null. 现在,它仅绑定您告诉它要绑定的内容,并且由于您不包括InvoiceLines集合,因此它变为null。

2) if you want ASP MVC to attempt to bind data into your model, you need to POST that data back to the server. 2)如果您希望ASP MVC尝试将数据绑定到模型中,则需要将该数据发布回服务器。 If those invoice lines aren't in an html form and aren't represented as form fields, then that data is just going to be missing. 如果这些发票行不是html格式,并且没有表示为表单字段,则该数据将丢失。

The code you have to draw your invoice lines is missing most of the fields that would be required to populate your InvoiceLine objects. 您必须绘制发票行的代码缺少填充InvoiceLine对象所需的大多数字段。

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

Editor Template for the InvoiceLine class InvoiceLine类的编辑器模板

In my sample code, I created an editor template for the InvoiceLine class. 在示例代码中,我为InvoiceLine类创建了一个编辑器模板。 This allows me to easily create bindable html for the InvoiceLines by calling @Html.EditorFor(model => model.Invoice.InvoiceLines) asdf 这使我可以通过调用@Html.EditorFor(model => model.Invoice.InvoiceLines) asdf轻松为InvoiceLines创建可绑定html。

I know this isn't the simplest answer ever but I hope this helps you over the hump. 我知道这不是有史以来最简单的答案,但我希望这对您有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM