CRUD с отношениями «Многие ко многим» в ядре Entity Framework

#c# #asp.net-core #entity-framework-core

#c# #asp.net-core #entity-framework-core

Вопрос:

У меня есть две модели и одна таблица соединений. Я хотел бы использовать тот же вид для редактирования и создания нового заказа на покупку при добавлении товаров в той же форме.

Модель PurchaseOrder:

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

Редактировать метод Post:

 [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.Имя вместо значения
  • Когда код достигает foreachloop для перебора элементов, он выдает исключение null.

Ответ №1:

Вы получаете имя только для недавно добавленных дочерних элементов, у вас нет идентификаторов. и получение нулевых значений, как в возвращаемых данных для идентификаторов реляционных таблиц дочерних данных, не инициализируется.

вы можете решить эту проблему несколькими способами в соответствии с пригодностью:

  • Добавляйте дочерние строки непосредственно из представления и обновляйте идентификаторы в модели, поэтому у вас всегда будут идентификаторы в данных, и вам не нужно будет сталкиваться с проблемой нулевых идентификаторов.

  • В качестве альтернативы, вам может потребоваться сначала добавить новые значения дочерней таблицы, а затем использовать новые идентификаторы в реляционных данных вручную перед сохранением изменений.

Ответ №2:

Проблема заключается в привязке модели, она основана на атрибуте name. Измените код, как показано ниже:

 @{ 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>