【问题标题】:.NET Core Web API Updating a model in the database context wont work.NET Core Web API 在数据库上下文中更新模型不起作用
【发布时间】:2020-01-22 05:08:18
【问题描述】:

我正在编写一个名为Cart.API 的小服务,我应该处理我的数据库上的所有添加/更新/删除请求(我使用的是 docker,因此我在 docker 容器上托管了一个 MSSQL 数据库)。现在,我被困在DeleteCatalogItemAsync的方法上。

这是CartController.cs的代码:

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Cart.API.Models;
using Cart.API.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace Cart.API.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class CartController : ControllerBase
    {
        private readonly CartContext _context;
        private readonly ICartService _cartService;
        private readonly ICatalogService _catalogService;

        public CartController(CartContext context, ICartService cartService, ICatalogService catalogService)
        {
            _context = context;
            _cartService = cartService;
            _catalogService = catalogService;
        }

        [HttpGet]
        [Route("cart/{id}")]
        public bool CartExists(int id)
        {
            return _context.UserCarts.Any(cart => cart.Id == id);
        }

        [HttpGet]
        [Route("entries")]
        public async Task<ActionResult<List<CartModel>>> GetAllCartAsync()
        {
            var cartList = await _context.UserCarts.Include(cart => cart.Items).ToListAsync(); //include needed to load Items List
            if (cartList == null)
            {
                return NoContent();
            }

            return cartList;
        }

        [HttpGet]
        [Route("entries/{id}")]
        public async Task<ActionResult<CartModel>> GetCartByIdAsync(int id)
        {
            var cart = await _context.UserCarts.Where(b => b.Id == id).Include(m => m.Items).SingleOrDefaultAsync();

            return cart ?? new CartModel(id);
        }

        [HttpPut]
        [Route("entries")]
        public async Task<ActionResult> AddCartItemAsync([FromBody] AddCartItemRequest requestItem)
        {
            if (requestItem == null || requestItem.Quantity == 0)
            {
                return BadRequest("Invalid payload");
            }

            //Check, if cart already exists or a new one has to be added
            var cartExists = await _cartService.CheckCartExistance(requestItem.CartId);

            //Get or create cart object by the requested Id
            var currentCart = await _cartService.GetCartByIdAsync(requestItem.CartId);

            //Get Catalog Item
            var catalogItem = await _catalogService.GetCatalogItemByIdAsync(requestItem.CatalogItemId);

            currentCart.Items.Add(new CartItem(catalogItem.Id, catalogItem.Name, catalogItem.Price, requestItem.Quantity));

            if (!cartExists)
            {
                _context.UserCarts.Add(currentCart);
            }
            else
            {
                _context.UserCarts.Update(currentCart);
            }

            await _context.SaveChangesAsync();

            return Ok(currentCart);
        }

        [HttpDelete]
        [Route("entries/remove")]
        public async Task<ActionResult> DeleteCatalogItemAsync([FromBody] DeleteCartItemRequest requestItem)
        {
            if (requestItem == null)
            {
                return BadRequest("Cannot perform action");
            }

            //Check if Basket exists
            var basketExists = await _cartService.CheckCartExistance(requestItem.CartId);

            if (basketExists)
            {
                //var currentCart = await _context.UserCarts.Include(model => model.Items).SingleOrDefaultAsync(model => model.Id == requestItem.CartId);

                var currentCart = await _cartService.GetCartByIdAsync(requestItem.CartId);
                if(currentCart != null)
                {
                    var itemToDelete = currentCart.Items.Find(model => model.ProductId == requestItem.CatalogItemId);
                    currentCart.Items.Remove(itemToDelete);
                    //currentCart.RemoveCartItem(requestItem.CatalogItemId);
                    _context.UserCarts.Update(currentCart);
                    await _context.SaveChangesAsync();
                    return Ok();
                }

                return BadRequest("Das Item mit der ID " + requestItem.CatalogItemId + " konnte nicht gelöscht werden.");
            }
            return NotFound("Es existiert kein Warenkorb mit der ID " + requestItem.CartId);
        }

        [HttpGet]
        [Route("test")]
        public async Task<ActionResult> TestCall()
        {
            var test1 = await _catalogService.GetCatalogItemByIdAsync(1);
            return Ok(test1);
        }
    }
}

CartService.cs:

using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Cart.API.Models;
using Newtonsoft.Json;

namespace Cart.API.Services
{
    public class CartService : ICartService
    {
        private readonly HttpClient _client;

        public CartService(HttpClient client)
        {
            _client = client;
        }

        public async Task<IEnumerable<CartModel>> GetAllCartsAsync()
        {
            var stringContent = await _client.GetStringAsync("http://cart.api/api/cart/entries");
            return JsonConvert.DeserializeObject<List<CartModel>>(stringContent);
        }

        public async Task<CartModel> GetCartByIdAsync(int id)
        {
            var stringContent = await _client.GetStringAsync("http://cart.api/api/cart/entries/" + id);
            var cart = !string.IsNullOrEmpty(stringContent) ? JsonConvert.DeserializeObject<CartModel>(stringContent) : null;
            return cart;
        }

        public async Task<bool> CheckCartExistance(int cartId)
        {
            var stringContent = await _client.GetStringAsync("http://cart.api/api/cart/cart/" + cartId);
            return JsonConvert.DeserializeObject<bool>(stringContent);
        }
    }
}

CartModel.cs

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;

namespace Cart.API.Models
{
    public class CartModel
    {
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int Id { get; set; }
        public List<CartItem> Items { get; set; }

        public CartModel(int id)
        {
            Id = id;
            Items = new List<CartItem>();
        }

        public bool RemoveCartItem(int id)
        {
            var itemToDelete = Items.Find(m => m.ProductId == id);
            return Items.Remove(itemToDelete);
        }

    }
}

CartItem.cs

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Cart.API.Models
{
    public class CartItem
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public long Id { get; set; }
        public long ProductId { get; set; }
        public string ProductName { get; set; }
        public double ProductPrice { get; set; }
        public int ProductQuantity { get; set; }
        public CartItem(long productId, string productName, double productPrice, int productQuantity)
        {
            ProductId = productId;
            ProductName = productName;
            ProductPrice = productPrice;
            ProductQuantity = productQuantity;
        }
    }
}

因此,在CartController.cs 的方法DeleteCatalogItemAsync 中,我试图提供能够删除现有CatalogItem 的功能。因此,在我实际从购物车列表中删除 CatalogItem 之前,我调用 cartService 来检索应该从中删除项目的购物篮。我的问题是,使用上面提供的代码,CatalogItem 没有被从数据库中删除,因为对 currentCart 对象的更改以某种方式没有保存到数据库中。据我所知,currentCart 对象是未跟踪的,所以在将对象保存到数据库之前,我调用 .Update 方法开始跟踪项目并将所有属性标记为已修改。

var itemToDelete = currentCart.Items.Find(model => model.ProductId == requestItem.CatalogItemId);
currentCart.Items.Remove(itemToDelete);
_context.UserCarts.Update(currentCart);
await _context.SaveChangesAsync();

我玩弄了上面的代码顺序,并且以某种方式将更改的对象保存到 dababase 可以使用以下代码顺序:

var itemToDelete = currentCart.Items.Find(model => model.ProductId == requestItem.CatalogItemId);
_context.UserCarts.Update(currentCart);
currentCart.Items.Remove(itemToDelete);
await _context.SaveChangesAsync();

为什么这个解决方案有效,但不是第一个?

【问题讨论】:

    标签: entity-framework asp.net-core-webapi


    【解决方案1】:

    下面这行代码会将实体的currentCart实例附加到DBContext,开始跟踪实体,并将实体状态设置为Modified

    _context.UserCarts.Update(currentCart);
    

    codet 的下一行currentCart.Items.Remove(itemToDelete) 将导致itemToDelete 实体状态为已删除。

    如果你打电话 currentCart.Items.Remove(itemToDelete); 在调用 context.UserCarts.Update(currentCart); 之前,因为 currentCart 对象未被 DbContext 跟踪,因此 itemToDelete 实体不会被标记为已删除。

    因此,EntityFramework 将无法在调用Save 方法时知道该实体需要被删除

    希望对你有帮助

    【讨论】:

    • 首先,感谢您的回复。看了之后,其实很有道理,但是……如果你看一下AddCartItemAsync方法,也有同样的情况。我有一个名为 currentCart 的未跟踪对象,在实际调用 .Update 之前,我在其中添加了 CatalogItems。这个订单运行良好.. 怎么样?我的意思是这是相同的情况,而不是删除,我添加项目..
    • AddCartItemAsync 方法中,当您调用 _context.SaveChangesAsync(); currentCart 时,其所有包含的购物车项目状态会根据购物车的存在进行修改或添加。在这种情况下,EF 框架可以确定如何处理 Cart 和 CartItem 实体。要删除实体,必须先对其进行跟踪,然后将其状态更改为Deleted
    猜你喜欢
    • 1970-01-01
    • 2017-05-15
    • 2013-03-19
    • 1970-01-01
    • 2019-01-12
    • 1970-01-01
    • 1970-01-01
    • 2020-01-26
    • 1970-01-01
    相关资源
    最近更新 更多