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" 属性使这一切变得容易得多。
我正在使用数据库优先方法。我有一个用于输入数据的表单。由此,将创建一个新 "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" 属性使这一切变得容易得多。