Entity Framework CoreがSaveChanges()が呼び出されたときに重複レコードを挿入しようとしています

c# entity-framework entity-framework-core

質問

私の主体は:

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

addメソッドは次のとおりです。

public async Task AddAsync(TEntity entity)
{
    await Task.Factory.StartNew(() => this.entities.Add(entity));
}

私が追加しているエンティティは次のとおりです。

ShoppingCartItem()
{
    CustomerId = "xxxxx",
    ProductId = 1,
    Customer = null,
    Product = null 
}

SaveChanges()を呼び出すと、EFはShoppingCartItem 2つの同一のレコードを挿入しようとしています。 ShoppingCartItemが作成され、コンテキストに一度だけ追加されます。おそらく何が間違っているのでしょうか?

編集:

これは私がAddSyncメソッドを呼び出す方法です:

public async Task AddNewCartItem(ShoppingCartItem shopingCartItem)
    {
        await this.ShoppingCartItemRepository.AddAsync(shopingCartItem);
        await this.SmartStoreWorkData.CompleteAsync();
    }

受け入れられた回答

更新:私は次のことをしました:

  • レポのリンクを複製SmartStoreNETCore
  • 新しいEFツールを使用して.NET Core 1.1に移行(preview4)
  • 以下のEF ModelBuilderで指定された設定を追加
  • 適用された移行と更新されたデータベース

コマンド:

dotnet ef --startup-project ../SmartStoreNetCore.Web/ migrations add ChangeShoppingCartItemKey
dotnet ef --startup-project ../SmartStoreNetCore.Web/ database update
  • _Layout.cshtml次の重複タグ_Layout.cshtml

    <script src="~/js/site.js" asp-append-version="true"></script>

site.jsは、 カートに追加機能のクリックイベントハンドラが含まれています

  • サイトを開始し、 すべてが期待通りに機能し、ショッピングカートのアイテムが重複せず、数量が期待どおりに更新されました

ここに画像の説明を入力

要約すれば

site.jsへの重複参照を削除する前に、次のメソッドが2回呼び出されことを完全に確認できます。

 public async Task AddNewCartItem(ShoppingCartItem shopingCartItem)
 {
     await this.ShoppingCartItemRepository.AddAsync(shopingCartItem);
     await this.SmartStoreWorkData.CompleteAsync();
 }

デバッグでこれを覚えていないのはなぜか私にはミステリーです

EF ModelBuilder

設定は次のようになります。

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

自動的に生成された値を持つことは、それを主キーとして定義しません


人気のある回答

DbContextスレッドセーフではありません 。コメントに記載されているように、あなたの.Add()を別のスレッドとする可能性が非常に高いので、あなたはDbContext混乱させるでしょう。 Add()は純粋にメモリ内操作です。それを非同期化しようとする理由はありません。それを変更し、私は問題を解決すると思う。

public void Add(TEntity entity)
{
    this.entities.Add(entity);
}

あなたの質問に表示されていない他の似たような使用法がある場合は、それらも同期するように変更してください。

あなたが「適切な」非同期操作を行うことができ DbContextそれだけで実際にはデータベースではなく、メモリ内のものに話をする方法のためであり、通常は関与しませんTask.<anything> 、単に提供するasyncメソッド。

編集 :完全性のために、上記のリンクはEF6ですが、EFコアではDbContextスレッドセーフではありません



Related

ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
このKBは合法ですか? はい、理由を学ぶ
ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
このKBは合法ですか? はい、理由を学ぶ