实体框架核心中具有多对多关系的CRUD



我有两个模型和一个联接表。我想使用相同的视图来编辑和创建新的采购订单,同时以相同的形式添加项目。

采购订单模型:

public class PurchaseOrder
{
public int ID { get; set; }
[Required]
public string Requester { get; set; }
public int WorkOrder { get; set; }
[Required]
public string WorkSite { get; set; }
public string Equipment { get; set; }
public string Operator { get; set; }
public string Remarks { get; set; }
public ICollection<PurchaseOrderItem> Items { get; set; }
}

商品型号:

public class Item
{
public int ID { get; set; }
public string PartNumber { get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; }
[Required]
public int Quantity { get; set; }
public string Remarks { get; set; }
public ICollection<PurchaseOrderItem> PurchaseOrders { get; set; }
}

联接表实体

public class PurchaseOrderItem
{
public int PurchaseOrderID { get; set; }
public int ItemID { get; set; }
public PurchaseOrder PurchaseOrder { get; set; }
public Item Item { get; set; }
}

PurchaseOrdersController编辑:

public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var purchaseOrder = await _context.PurchaseOrders
.Include(x => x.Items)
.ThenInclude(x => x.Item)
.Where(x => x.ID == id)
.AsNoTracking()
.SingleOrDefaultAsync();
if (purchaseOrder == null)
{
return NotFound();
}
return View(purchaseOrder);
}

编辑帖子方法:

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, PurchaseOrder order)
{
if (id != order.ID)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
var po = _context.PurchaseOrders.FirstOrDefault(i => i.ID == order.ID);
po.Requester = order.Requester;
po.WorkOrder = order.WorkOrder;
po.WorkSite = order.WorkSite;
po.Equipment = order.Equipment;
po.Operator = order.Operator;
po.Remarks = order.Remarks;
_context.Update(po);

foreach (var i in order.Items)
{
var item = _context.Items.FirstOrDefault(n => n.ID == i.Item.ID);
item.PartNumber = i.Item.PartNumber;
item.Name = i.Item.Name;
item.Description = i.Item.Description;
item.Quantity = i.Item.Quantity;
item.Remarks = i.Item.Remarks;
_context.Update(item);
}
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!PurchaseOrderExists(order.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(order);
}

编辑视图

@model OrderTrackingApp.Models.PurchaseOrder
@{
ViewData["Title"] = "Edit";
}
<h1>Edit</h1>
<h4>PurchaseOrder</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="ID" />
<div class="form-group">
<label asp-for="Requester" class="control-label"></label>
<input asp-for="Requester" class="form-control" />
<span asp-validation-for="Requester" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="WorkOrder" class="control-label"></label>
<input asp-for="WorkOrder" class="form-control" />
<span asp-validation-for="WorkOrder" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="WorkSite" class="control-label"></label>
<input asp-for="WorkSite" class="form-control" />
<span asp-validation-for="WorkSite" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Equipment" class="control-label"></label>
<input asp-for="Equipment" class="form-control" />
<span asp-validation-for="Equipment" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Operator" class="control-label"></label>
<input asp-for="Operator" class="form-control" />
<span asp-validation-for="Operator" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Remarks" class="control-label"></label>
<input asp-for="Remarks" class="form-control" />
<span asp-validation-for="Remarks" class="text-danger"></span>
</div>
<div class="form-group">
<label class="control-label">Order Items</label>
<table>
<tbody>
@{ int i = 0;}
<tr>
@foreach (var OrderItem in Model.Items)
{
<td>
<input type="hidden" value="OrderItem[@i].ItemID" asp-for="@OrderItem.ItemID"
class="form-control" />
<input type="text" value="OrderItem[@i].Item.PartNumber" asp-for="@OrderItem.Item.PartNumber" class="form-control" />
<input type="text" value="OrderItem[@i].Item.Name" asp-for="@OrderItem.Item.Name" />
</td>
i++;
}
</tr>
</tbody>
</table>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>

</form>
</div>
</div>


<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

我目前有两个编辑问题:

  • 项的标签填充如下:OrderItem[0].item.Name而不是Value
  • 当代码到达foreachloop对项进行迭代时,它抛出了一个null异常

您只获得名称,因为对于新添加的子项,您没有ID。以及在子数据的关系表id的返回数据中获取null值没有初始化。

根据适用性,您可以通过两种方式解决此问题:

  • 直接从视图中添加子行并更新模型中的id,因此将始终在数据中具有id,并且不需要face null id问题

  • 或者,您可能需要首先添加子表的新值然后在保存更改之前,手动在关系数据中使用新的id。

问题是模型绑定,它基于名称属性。更改代码如下:

@{ int i = 0;}
<tr>
@foreach (var OrderItem in Model.Items)
{
<td>
<input type="hidden" name="Items[@i].Item.ID" asp-for="@OrderItem.Item.ID"
class="form-control" />
<input type="text" name="Items[@i].Item.PartNumber" asp-for="@OrderItem.Item.PartNumber" class="form-control" />
<input type="text" name="Items[@i].Item.Name" asp-for="@OrderItem.Item.Name" class="form-control" />
</td>
i++;
}
</tr>