Reputation: 567
My Entities are:
public class Customer
{
...
public virtual ICollection<ShoppingCartItem> ShoppingCartItems { get; set; }
...
}
public class ShoppingCartItem
{
public string CustomerId { get; set; }
public int ProductId { get; set; }
public virtual Customer { get; set; }
public virtual Product{ get; set; }
...
}
The add method is:
public async Task AddAsync(TEntity entity)
{
await Task.Factory.StartNew(() => this.entities.Add(entity));
}
The entity that I am adding is:
ShoppingCartItem()
{
CustomerId = "xxxxx",
ProductId = 1,
Customer = null,
Product = null
}
When I call SaveChanges() the EF is trying to insert two identical records for ShoppingCartItem
. The ShoppingCartItem
is created and added to the context only once. Any ideas what possibly could be wrong?
EDIT:
This is how I call the AddSync method:
public async Task AddNewCartItem(ShoppingCartItem shopingCartItem)
{
await this.ShoppingCartItemRepository.AddAsync(shopingCartItem);
await this.SmartStoreWorkData.CompleteAsync();
}
Upvotes: 2
Views: 9165
Reputation: 1537
UPDATE: I did the following:
Commands:
dotnet ef --startup-project ../SmartStoreNetCore.Web/ migrations add ChangeShoppingCartItemKey
dotnet ef --startup-project ../SmartStoreNetCore.Web/ database update
Removed the following duplicate tag in _Layout.cshtml
<script src="~/js/site.js" asp-append-version="true"></script>
site.js
contains the click event handler for the Add To Cart functionality
I can fully confirm the following method was being called twice before removing the duplicate reference to
site.js
:
public async Task AddNewCartItem(ShoppingCartItem shopingCartItem)
{
await this.ShoppingCartItemRepository.AddAsync(shopingCartItem);
await this.SmartStoreWorkData.CompleteAsync();
}
It is a mistery to me why you didn't catch this before via debugging
Your configuration should look like:
builder.Entity<ShoppingCartItem>().HasKey(x => x.Id); // Notice this!
builder.Entity<ShoppingCartItem>().Property(x => x.Id).ValueGeneratedOnAdd(); // Also this!
builder.Entity<ShoppingCartItem>().HasOne(s => s.Customer).WithMany(b => b.ShoppingCartItems).OnDelete(DeleteBehavior.Restrict);
builder.Entity<ShoppingCartItem>().HasOne(s => s.Product).WithMany().OnDelete(DeleteBehavior.Restrict);
Having a value generated automatically does not define it as the primary key
Upvotes: 2
Reputation: 8335
DbContext
is not thread safe. By - pointlessly, as noted in the comments - performing your .Add()
in what is quite likely a different thread, you're confusing the DbContext
. Add()
is purely an in-memory operation; there is no reason to try to make it async. Change that and I reckon it will resolve the issue.
public void Add(TEntity entity)
{
this.entities.Add(entity);
}
If you have any other similar usages that are not shown in your question, change those to sync too.
You can do "proper" async with DbContext
but it is only for the methods that actually talk to the database, not the in-memory ones, and will not usually involve Task.<anything>
, just the provided async
methods.
Edit: for completeness, the link above is for EF6, but in EF Core, DbContext
is also not thread-safe.
Upvotes: 3