Search code examples
c#.net-coreentity-framework-coreentity-framework-6

EF Core 6, creates new parent record when trying to create only child record


Here are my models:

public class ShoppingCart
{
    public CartHeader CartHeader { get; set; }
    public IEnumerable<CartDetails> CartDetails { get; set; }
}

public class CartHeader
{
    [Key]
    public int CartHeaderId { get; set; }
    public string UserId { get; set; }
    public string? CouponCode { get; set; }
}

public class CartDetails
{
    public int CartDetailsID { get; set; }
    public int CartHeaderId { get; set; }

    [ForeignKey("CartHeaderId")]
    public virtual CartHeader CartHeader { get; set; }

    public int ProductId { get; set; }

    [ForeignKey("ProductId")]
    public virtual Product Product { get; set; }

    public int Count { get; set; }
}

Here is my code which populates the CartHeader and CartDetails tables:

var cartHeaderFromDb = await _dbContext.CartHeaders.AsNoTracking()
                    .FirstOrDefaultAsync(u => u.UserId == shoppingCart.CartHeader.UserId);

if (cartHeaderFromDb == null)
{
    //create header and details
    /// This creates CartHeader and CartDetails Records
    _dbContext.CartDetails.Add(shoppingCart.CartDetails.FirstOrDefault());
    await _dbContext.SaveChangesAsync();
}
else
{
    //if header is not null
    //check if details has same product
    /// This block should only creates CartDetails and not create CartHeader, but it does
    var cartDetailsFromDb = _dbContext.CartDetails.AsNoTracking().Where(u =>
        u.CartHeaderId == cartHeaderFromDb.CartHeaderId).ToList();

    if (!cartDetailsFromDb.Exists(c => c.ProductId == shoppingCart.CartDetails.First().ProductId))
    {
        //create details                       
        shoppingCart.CartHeader = null;
        shoppingCart.CartDetails.FirstOrDefault().CartHeaderId = cartHeaderFromDb.CartHeaderId;
        _dbContext.CartDetails.Add(shoppingCart.CartDetails.FirstOrDefault());
        await _dbContext.SaveChangesAsync();
    }
    else
    {
        //update the count / cart details
        shoppingCart.CartHeader = null;
        shoppingCart.CartDetails.FirstOrDefault().CartHeaderId = cartHeaderFromDb.CartHeaderId;
        shoppingCart.CartDetails.FirstOrDefault().Count += cartDetailsFromDb.Count;
        _dbContext.CartDetails.Update(shoppingCart.CartDetails.FirstOrDefault());
        await _dbContext.SaveChangesAsync();
    }
}

Here both if and else blocks create another CartHeader record which I don't want. I want to use the same CartHeader ID, if there exists one. Tried AsNoTracking() at various places and setting navigation properties to null as well, no success! It always creates another CartHeader record and the new CartDetails gets that new CartHeader ID. I cannot figure out what I am missing here.


Solution

  • For anyone facing the same problem, I was able to resolve the issue using the following code.

    _dbContext.CartDetails.Add(new CartDetails()
    {
        CartHeaderId = cartHeaderFromDb.CartHeaderId,
        ProductId = shoppingCart.CartDetails.First().ProductId,
        Count = shoppingCart.CartDetails.First().Count
    }
    await _dbContext.SaveChangesAsync();
    

    With this code, no new CartHeader records are being created.

    Comments are welcome to improve it. Thanks