ASP.NET MVC EntityFramework 如何在 Create 中保存新对象和多个子对象

ASP.NET MVC EntityFramework How to Save New Object along with Multiple Child Objects in Create

我正在使用数据库优先方法。我有一个用于输入数据的表单。由此,将创建一个新 "form entry"、一个新客户和 3 个新地址。 2个地址与客户关联,1个地址与表单条目整体关联,客户与表单条目关联。

问题(修订版2): 通常如何准备引用将同时创建的对象的字段 -- "hey, these id fields right here will reference objects created simultaneously from the same, current dataset you're being built from"?

(

即人们通常如何在 MVC 中执行以下操作:

1) 将3个地址中的每一个都创建为新地址;

2) 通过 checkoutForm.Customer.HomeAddressId 和 checkoutForm.Customer.MailingAddressId 将他们中的 2 个关联到一个新客户;

3) 将对方与checkoutForm.PlaceOfUseAddressId自身联系起来;

4) 挽救客户;然后

5) 将客户关联到checkoutForm.CustomerId;最后

6) 保存 checkoutForm 本身,

同时不使 ModelState.IsValid 为假,因为 checkoutForm.CustomerId 和每个 x.AddressId 都是必需的,但最初为空?

)

任何对解释像这样同时创建多个依赖对象的过程的引用都很棒!


编辑: 我从 Create() 的参数中删除了绑定属性,这使得所有数据都进入了正确的字段。下拉列表正确地获取了它们各自的 StateId。

目前,ModelState.IsValid 在提交时报告错误。子对象所需的 ID 为 null 等(这是有道理的,因为这就是我试图弄清楚如何告诉 .NET 的东西——这是一个对象关联,一旦我们拥有数据就会创建)。


Edit2: 我再次完善了这个问题,现在我越来越接近我实际需要解决的问题:ModelState.IsValid == false,因为引用依赖项的 checkoutForm 和 Customer 的相关 ID 为空。


此表格背后的模型描述:

这是表格本身:

( 注意 2 个下拉列表。其他所有内容都是字符串、整数或日期的文本框。 )

到目前为止,我对控制器的了解(不超出脚手架创建的范围):

// GET: CheckoutForms/Create
public ActionResult Create()
{
    ViewBag.HomeStateList = new SelectList(db.RT_STATE_LIST, "ST_SEQ", "ST_ABBR", 2);
    ViewBag.MailingStateList = new SelectList(db.RT_STATE_LIST, "ST_SEQ", "ST_ABBR", 2);
    return View();
}

// POST: CheckoutForms/Create
[HttpPost]
[ValidateAntiForgeryToken]
// public ActionResult Create([Bind(Include = "Customer.FirstName,Customer.LastName,VacuumNumber,Customer.Phone,Customer.DriversLicense,Customer.HomeAddress.Street,Customer.HomeAddress.City,Customer.HomeAddress.StateId,Customer.MailingAddress.Street,Customer.MailingAddress.City,Customer.MailingAddress.StateId,Customer.MailingAddress.PostalCode,PlaceOfUseAddress.Street,PlaceOfUseAddress.City,PlaceOfUseAddress.StateId,PickupDate,DueDate,ReturnedDate,EnteredBy,EnteredDate,ModifiedBy,ModifiedDate")] CheckoutForm checkoutForm)
public ActionResult Create(CheckoutForm checkoutForm)
{
    if (ModelState.IsValid)
    {
            checkoutForm.PlaceOfUseAddress.StateId = 1;

            // !!! Need to be given correct value once authorization is set up.
            checkoutForm.EnteredBy = -1;
            checkoutForm.ModifiedBy = -1;
            checkoutForm.EnteredDate = System.DateTime.Now;
            checkoutForm.ModifiedDate = System.DateTime.Now;

            // ??? db.Addresses.Add(checkoutForm.PlaceOfUseAddress);
            // ??? db.Addresses.Add(checkoutForm.Customer.HomeAddress);
            // ??? db.Addresses.Add(checkoutForm.Customer.MailingAddress);
            // ??? db.SaveChanges();
            // ??? checkoutForm.PlaceOfUseAddressId = checkoutForm.PlaceOfUseAddress.AddressId;
            // ??? checkoutForm.Customer.HomeAddressId = checkoutForm.Customer.HomeAddress.AddressId;
            // ??? checkoutForm.Customer.MailingAddressId = checkoutForm.Customer.MailingAddress.AddressId;
            // ??? db.Customers.Add(checkoutForm.Customer);
            // ??? db.SaveChanges();
            db.CheckoutForms.Add(checkoutForm);
      db.SaveChanges();
      return RedirectToAction("Index");
    }

        ViewBag.HomeStateList = new SelectList(db.RT_STATE_LIST, "ST_SEQ", "ST_ABBR", checkoutForm.Customer.HomeAddress.StateId);
        ViewBag.MailingStateList = new SelectList(db.RT_STATE_LIST, "ST_SEQ", "ST_ABBR", checkoutForm.Customer.MailingAddress.StateId);
        return View(checkoutForm);
}

最后,这是视图中的表单元素(删除了所有其他内容:

@using (Html.BeginForm()) 
{
    @Html.HiddenFor(model => model.CustomerId)
    @Html.HiddenFor(model => model.PlaceOfUseAddressId)
    @Html.HiddenFor(model => model.Customer.HomeAddressId)
    @Html.HiddenFor(model => model.Customer.MailingAddressId)
    @Html.HiddenFor(model => model.EnteredBy)
    @Html.HiddenFor(model => model.EnteredDate)
    @Html.HiddenFor(model => model.ModifiedBy)
    @Html.HiddenFor(model => model.ModifiedDate)


    <!-- Name and Vacuum Number -->
    @Html.EditorFor(model => model.Customer.FirstName, new { htmlAttributes = new { @class = "form-control checkout" } })

    @Html.EditorFor(model => model.Customer.LastName, new { htmlAttributes = new { @class = "form-control checkout" } })

    @Html.EditorFor(model => model.VacuumNumber, new { htmlAttributes = new { @class = "form-control checkout" } })

    <!-- Driver's License -->
    <!-- Phone Number -->
    @Html.EditorFor(model => model.Customer.Phone, new { htmlAttributes = new { @class = "form-control checkout" } })

    @Html.EditorFor(model => model.Customer.DriversLicense, new { htmlAttributes = new { @class = "form-control checkout" } })

    <!-- Place of Use Address -->
    @Html.EditorFor(model => model.PlaceOfUseAddress.Street, new { htmlAttributes = new { @class = "form-control checkout" } })

    @Html.EditorFor(model => model.PlaceOfUseAddress.City, new { htmlAttributes = new { @class = "form-control checkout" } })

    <!-- Customer's Home Address -->
    @Html.EditorFor(model => model.Customer.HomeAddress.Street, new { htmlAttributes = new { @class = "form-control checkout" } })

    @Html.EditorFor(model => model.Customer.HomeAddress.City, new { htmlAttributes = new { @class = "form-control checkout" } })

    @Html.DropDownListFor(model => model.Customer.HomeAddress.StateId, ViewBag.HomeStateList as SelectList, htmlAttributes: new { @class = "form-control checkout" })

    <!-- Customer's Mailing Address -->
    @Html.EditorFor(model => model.Customer.MailingAddress.Street, new { htmlAttributes = new { @class = "form-control checkout" } })

    @Html.EditorFor(model => model.Customer.MailingAddress.City, new { htmlAttributes = new { @class = "form-control checkout" } })

    @Html.DropDownListFor(model => model.Customer.MailingAddress.StateId, ViewBag.MailingStateList as SelectList, htmlAttributes: new { @class = "form-control checkout" })

    @Html.EditorFor(model => model.Customer.MailingAddress.PostalCode, new { htmlAttributes = new { @class = "form-control checkout" } })

    <!-- Dates Picked Up, Due, and Returned -->
    @Html.EditorFor(model => model.PickupDate, new { htmlAttributes = new { @class = "form-control checkout" } })

    @Html.EditorFor(model => model.DueDate, new { htmlAttributes = new { @class = "form-control checkout" } })

    @Html.EditorFor(model => model.ReturnedDate, new { htmlAttributes = new { @class = "form-control checkout" } })
}

想通了。

在视图上,隐藏字段与外键地址 ID 连接(正如我所拥有的),但也必须 pre-populated 具有虚假值(因此它们不为空),就像这样:

@Html.HiddenFor(model => model.CustomerId, new { @Value = "0" })
@Html.HiddenFor(model => model.PlaceOfUseAddressId, new { @Value = "0"})
@Html.HiddenFor(model => model.Customer.HomeAddressId, new { @Value = "0" })
@Html.HiddenFor(model => model.Customer.MailingAddressId, new { @Value = "0" })

然后,Controller 上的过程最终变得相当简单,因为 MVC 在其他方面成功地处理了所有部分。我只需要保存并关联它们,children on up:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(CheckoutForm checkoutForm)
{
    Address PlaceOfUse = new Address();
    Address HomeAddy = new Address();
    Address MailingAddy = new Address();
    Customer Client = new Customer();

    if (ModelState.IsValid)
    {
        // Not currently available to the user to be filled in by them
        checkoutForm.PlaceOfUseAddress.StateId = 1;

        // !!! Need to be given correct value once authorization is set up.
        checkoutForm.EnteredBy = TestAccountId;
        checkoutForm.ModifiedBy = TestAccountId;
        checkoutForm.EnteredDate = System.DateTime.Now;
        checkoutForm.ModifiedDate = System.DateTime.Now;

        PlaceOfUse = checkoutForm.PlaceOfUseAddress;
        HomeAddy = checkoutForm.Customer.HomeAddress;
        MailingAddy = checkoutForm.Customer.MailingAddress;
        db.Addresses.Add(PlaceOfUse);
        db.Addresses.Add(HomeAddy);
        db.Addresses.Add(MailingAddy);
        db.SaveChanges();
        checkoutForm.PlaceOfUseAddressId = PlaceOfUse.AddressId;
        checkoutForm.Customer.HomeAddressId = HomeAddy.AddressId;
        checkoutForm.Customer.MailingAddressId = MailingAddy.AddressId;

        Client = checkoutForm.Customer;
        db.Customers.Add(Client);
        db.SaveChanges();
        checkoutForm.CustomerId = Client.CustomerId;
        db.CheckoutForms.Add(checkoutForm);
        db.SaveChanges();
        return RedirectToAction("Index");
    } 
    else {
        // To review in debug mode, when there are problems
        //var errors = this.ModelState.Keys.SelectMany(key => this.ModelState[key].Errors);

        ViewBag.HomeStateList = new SelectList(db.RT_STATE_LIST, "ST_SEQ", "ST_ABBR", checkoutForm.Customer.HomeAddress.StateId);
        ViewBag.MailingStateList = new SelectList(db.RT_STATE_LIST, "ST_SEQ", "ST_ABBR", checkoutForm.Customer.MailingAddress.StateId);
        return View(checkoutForm);
    }
}

另外,从 Create() 的参数中删除 "Bind" 属性使这一切变得容易得多。