
多對多關係不像其他關係那麼簡單,在這篇文章中,我將向您展示如何建立多對多關係以及如何在 EF Core 中使用它們。
模型
多對多的簡單而實用的例子可能是某種數位電子商務商店。使用者可以將商品放入購物車(一個購物車可以有多個商品),而商品屬於多個購物車。讓我們從建立 Cart 和 Item 類別開始。
public class Cart
{
public int Id { get; set; }
public ICollection<Item> Items { get; set; }
}
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public int Quantity { get; set; }
public ICollection<Cart> Carts { get; set; }
}
這看起來不錯,但它不起作用。在本文發表時,EF Core 無法處理這種情況。看起來 EF Core 不知道如何處理這種關係,當您嘗試新增遷移時,您會得到以下結果:
Unable to determine the relationship represented by navigation property ‘Cart.Items’ of type ‘ICollection
- ’. Either manually configure the relationship, or ignore this property using the ‘[NotMapped]’ attribute or by using ‘EntityTypeBuilder.Ignore’ in ‘OnModelCreating’.
【無法確定類型為「ICollection<Item>」的導覽屬性「Cart.Items」表示的關係。請手動設定關係,或使用「[NotMapped]」屬性或在「OnModelCreating」中使用「EntityTypeBuilder.Ignore」忽略此屬性。】
我們需要做的第一件事是手動建立另一個「中間」類別(資料表),它將建立 Cart 和 Item 的多對多關係,讓我們建立這個類別:
public class CartItem
{
public int CartId { get; set; }
public Cart Cart { get; set; }
public int ItemId { get; set; }
public Item Item { get; set; }
}
我們建立了關聯 Cart 和 Item 的新類別 CartItem,我們還需要更改它們各自的導覽屬性:
public class Cart
{
public int Id { get; set; }
public ICollection<CartItem> Items { get; set; }
}
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public int Quantity { get; set; }
public ICollection<CartItem> Carts { get; set; }
}
如果您現在嘗試新增遷移,則會出現另一個錯誤:
The entity type ‘CartItem’ requires a primary key to be defined.
【實體類型「CartItem」需要定義一個主鍵。】
對,CartItem 沒有主鍵,由於它是多對多關係,因此它應該具有複合主鍵。複合主鍵類似於常規主鍵,但它由兩個屬性(資料行)而不是一個屬性組成。目前,建立複合鍵的唯一方法是在 OnModelCreating 中。
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<CartItem>().HasKey(i => new { i.CartId, i.ItemId });
}
最後,我們的資料庫結構可以由 EntityFramework 處理,我們可以繼續遷移了。
插入多對多
假設我們已經有 Cart 和 Item 在我們的資料庫中,現在我們想將特定商品 (Item) 新增到特定購物車 (Cart),為了做到這一點,我們需要建立新的 CartItem 並儲存它。
var cart = db.Carts.First(i => i.Id == 256);
var item = db.Items.First(i => i.Id == 1024);
// 可以使用兩個類別的主鍵ID進行關聯
var cartItem = new CartItem
{
CartId = cart.Id,
ItemId = item.Id
};
// 也可以使用兩個類別實體進行關聯
var cartItem = new CartItem
{
Cart = cart,
Item = item
};
db.Add(cartItem);
db.SaveChanges();
在多對多中檢索相關資料
從資料庫中取得資料相當簡單,注意使用 Include 關聯檢索相關資料。這裡總共涉及 3 個資料表:Cart、Item、CartItem(將商品 Item 與購物車 Cart 關聯起來)。
// 取得關聯所有商品的指定購物車
var cartIncludingItems = db.Carts.Include(cart => cart.Items).ThenInclude(row => row.Item).First(cart => cart.Id == 1);
// 取得指定購物車的所有商品
var cartItems = cartIncludingItems.Items.Select(row => row.Item);
另外,有些操作可以不使用關係來執行。例如,如果您有購物車 ID,則可以使用以下 Linq 一次取得所有商品:
var cartId = 1;
var cartItems = db.Items.Where(item => item.Carts.Any(j => j.CartId == cartId));
相同的原則適用於相反的用例,這意味著您可以套用上述模式來取得具有特定項目的所有購物車。
從多對多中刪除
刪除是指刪除購物車 Cart 和商品 Item 之間的關係 CartItem。在以下範例中,我們不會刪除購物車 Cart 或商品 Item,只會刪除購物車 Cart 和商品 Item 之間的關係 CartItem。
讓我們從購物車 Cart 中刪除單一產品 Item 開始。
var cartId = 1;
var itemId = 1;
var cartItem = db.CartsItems.First(row => row.CartId == cartId && row.ItemId == itemId);
db.Remove(cartItem);
db.SaveChanges();
然後,讓我向您展示如何從購物車中刪除所有項目。
var cart = db.Carts.Include(c=> c.Items).First(i => i.Id == 2);
db.RemoveRange(cart.Items);
db.SaveChanges();
原文作者:Zbigniew
原文標題:How to handle Many-To-Many in Entity Framework Core
原文連結:https://softdevpractice.com/blog/many-to-many-ef-core/
翻譯:沙漠盡頭的狼