Search code examples
c#jqueryasp.net-mvcasp.net-coremodel-view-controller

CRUD Operation for one to many relationship ViewModel in ASP.NET MVC


I have been looking through the net for the past few days but still couldn't get what I want. Maybe is my way for researching is wrong. But here's the problem I have an Order Model

public class Order
    {
        [Key]
        public int Id { get; set; }

        public int TotalItems { get; set; }

        public DateTime DeliveryDate { get; set; }

        public string OrderNumber { get; set; }
        public string DeliveryAddress { get; set; }

        [ForeignKey("ClientId")]
        public int ClientId { get; set; }
        

        public int OrderItemId { get; set; }
        [ForeignKey("OrderItemId")]

        public List<OrderItem> OrderItems { get; set; }
        public Client Client { get; set; }
    }

And an OrderItem model

public class OrderItem
    {
        public int Id { get; set; }

        [ForeignKey("OrderId")]
        public int OrderId{ get; set; }

        public int InventoryInfoId { get; set; }
        [ForeignKey("InventoryInfoId")]

        [Required]
        public string ItemCode { get; set; }

        public int Quantity { get; set; }
        public virtual InventoryInfo InventoryInfo { get; set; }
        public Order Order { get; set; }
    }

One Order can have many OrderItems base on the users input. This is my OrderViewModel

public class OrderViewModel
    {
        //public int OrderId { get; set; }
        //public string OrderNumber { get; set; }
        //public string DeliveryAddress { get; set; }
        //public int ClientId { get; set; }
        //public DateTime Deliverydate { get; set; }
        public Order Order { get; set; }
        public IList<OrderItem> OrderItems { get; set; }
        public int ItemCode { get; set; }
        public int Quantity { get; set; }
    }

Here's my Create.cshtml

<div class="row">
    <div class="col-md-4">
        <form asp-action="Create">
            <div asp-validation-summary="All" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Order.ClientId" class="control-label"></label>
                <select asp-for="Order.ClientId" class="form-control" asp-items="ViewBag.ClientId">
                    <option>-- Select Client --</option>
                </select>
                <span asp-validation-for="Order.ClientId" class="text-danger"></span>

            </div>
            <div class="form-group">
                <label asp-for="Order.OrderNumber" class="control-label"></label>
                <input asp-for="Order.OrderNumber" class="form-control" />
                <span asp-validation-for="Order.OrderNumber" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Order.DeliveryDate" class="control-label"></label>
                <input type="date" data-val="true" asp-for="Order.DeliveryDate" class="form-control" />
                <span asp-validation-for="Order.DeliveryDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Order.DeliveryAddress" class="control-label"></label>
                <input asp-for="Order.DeliveryAddress" class="form-control" />
                <span asp-validation-for="Order.DeliveryAddress" class="text-danger"></span>
            </div>

            <table id="tblCustomers" class="table" cellpadding="0" cellspacing="0">
                <thead>
                    <tr>
                        <th style="width:150px">Item Code</th>
                        <th style="width:150px">Quantity</th>
                        <th></th>
                    </tr>
                </thead>
                <tbody></tbody>
                <tfoot id="item-list">
                    <tr>
                        <td>
                            <select asp-for="OrderItems[0].ItemCode" class="items" asp-items="@ViewBag.Item"></select>
                        </td>
                        <td><input type="text" asp-for="OrderItems[0].Quantity" class="items" /></td>
                        @*<td><input type="button" id="btnAdd" value="Add" /></td>*@
                    </tr>
                </tfoot>
            </table>
            <button id="add">Add another item</button>

            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>

        </form>
    </div>

</div>



@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
    <script>
        $("#add").click(function (e) {
           e.preventDefault();
           var i = ($(".items").length) / 2;
           var model = @Html.Raw(@ViewBag.Items);
            var n = '<tr><td><select id="OrderItems_' + 1 + '_ItemCode" name="OrderItems[' + 1 + '].ItemCode" class="items" /></td>' +
            '<td><input type="text" class="items" name="OrderItems[' + 1 + '].Quantity" /></td></tr>'

           $("#item-list").append(n);

           var Items = "";
           $(model).each(function (e) {
               Items = Items + '<option value="' + this.Value + '">' + this.Text + '</option>'
           });

           var subItemList = $("#OrderItems" + 1 + "_ItemCode");
            subItemList.empty();
            subItemList.append(Items);
    });
    </script>

}

There are two parts for the user, Order Details and Order Items And here is my OrdersController Create

[HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Create([Bind("ClientId", "OrderNumber", "DeliveryDate", "DeliveryAddress")]OrderViewModel OrderVM)
        {

            try
            {
                if (ModelState.IsValid)
                {
                    var order = new Order()
                    {
                        Id = OrderVM.OrderId,
                        DeliveryAddress = OrderVM.DeliveryAddress,
                        DeliveryDate = OrderVM.Deliverydate,
                        OrderNumber = OrderVM.OrderNumber
                    };
                    _context.Add(order);
                    await _context.SaveChangesAsync();
                    return RedirectToAction(nameof(Index));
                }
            }
            catch (DbUpdateException ex)
            {
                //Log the error(uncomment ex variable name and write a log.
                ModelState.AddModelError("", "Unable to save changes. " +
                    "Try again, and if the problem persists " +
                    "see your system administrator.");
            }
            PopulateClientDropDownList(OrderVM.ClientId);
            CreateMultipleOrderItem();
            return View(OrderVM);
        }

I'm actually so lost right now as I have tried several tutorials and look through examples but it seems that nothing can help me with this. Maybe I couldn't understand them. Please help me guys!

With the help of Jeffery.

[HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Create(OrderViewModel OrderVM)
        {

            try
            {
                if (ModelState.IsValid)
                {
                    //var order = new Order()
                    //{
                    //    Id = OrderVM.OrderId,
                    //    DeliveryAddress = OrderVM.DeliveryAddress,
                    //    DeliveryDate = OrderVM.Deliverydate,
                    //    OrderNumber = OrderVM.OrderNumber
                    //};
                    _context.AddRange(OrderVM);
                    
                    //_context.AddRange(OrderVM.OrderItems);
                    await _context.SaveChangesAsync();
                    return RedirectToAction(nameof(Index));
                }
            }
            catch (DbUpdateException ex)
            {
                //Log the error(uncomment ex variable name and write a log.
                ModelState.AddModelError("", "Unable to save changes. " +
                    "Try again, and if the problem persists " +
                    "see your system administrator.");
            }
            PopulateClientDropDownList(OrderVM.Order.ClientId);
            CreateMultipleOrderItem();
            return View(OrderVM);
        }

The edited version of the controller allows the data to pass from my view page to the controller. However there's an error.InvalidOperationException: The entity type 'OrderViewModel' was not found. Ensure that the entity type has been added to the model. Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.GetOrCreateEntry(object entity)


Solution

  • Try something like this

        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Create(OrderViewModel OrderVM)
        {
    
            try
            {
                if (ModelState.IsValid)
                {
                    var order = new Order()
                    {
                        // Id = OrderVM.OrderId,- Primary keys will be auto generated
                        DeliveryAddress = OrderVM.DeliveryAddress,
                        DeliveryDate = OrderVM.Deliverydate,
                        OrderNumber = OrderVM.OrderNumber,
                        ClientId = OrderVM.ClientId,
                        TotalItems = OrderVM.TotalItems,
                        OrderItems = OrderVM.Select(item => new OrderItem()
                        {
                         ItemCode = item.ItemCode,
                         Quantity = item.Quantity,
                         InventoryInfoId = item.InventoryInfoId
          
                        }).ToList()
                    };
                    await _context.Orders.AddAsync(order);
                    
                    //_context.AddRange(OrderVM.OrderItems);
                    await _context.SaveChangesAsync();
    
                    return RedirectToAction(nameof(Index));
                }
            }
            catch (DbUpdateException ex)
            {
                //Log the error(uncomment ex variable name and write a log.
                ModelState.AddModelError("", "Unable to save changes. " +
                    "Try again, and if the problem persists " +
                    "see your system administrator.");
            }
            PopulateClientDropDownList(OrderVM.Order.ClientId);
            CreateMultipleOrderItem();
            return View(OrderVM);
        }