How to handle the many-to-many relationship of EF Core?

How to handle the many-to-many relationship of EF Core?

Many-to-many relationships are not as simple as other relationships, and in this article I will show you how to create many-to-many relationships and how to use them in EF Core.

最后更新 11/2/2021 8:47 PM
Zbigniew
预计阅读 6 分钟
分类
EF Core
标签
.NET EF Core ORM

Many-to-many relationships are not as simple as other relationships, and in this article I will show you how to create many-to-many relationships and how to use them in EF Core.

model

多对多的简单而实用的例子可能是某种数字电子商务商店。用户可以将商品放入购物车(一个购物车可以有多个商品),而商品属于多个购物车。让我们从创建CartItem类开始。

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

It looks good, but it doesn't work. At the time of publication, EF Core was unable to handle this situation. It seems that EF Core doesn't know how to handle this relationship, and when you try to add a migration, you get the following results:

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’.

[The relationship represented by the navigation property "Cart.Items" of type "ICollection Item" cannot be determined. Configure the relationship manually, or use the "[NotMapped]" attribute or use the "EntityTypeBuilder.Ignore" in "OnModelCreating" to ignore this attribute.]

我们需要做的第一件事是手动创建另一个“中间”类(表),它将建立CartItem的多对多关系,让我们创建这个类:

public class CartItem
{
    public int CartId { get; set; }
    public Cart Cart { get; set; }

    public int ItemId { get; set; }
    public Item Item { get; set; }
}

我们创建了关联CartItem的新类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; }
}

If you try to add a migration now, another error will occur:

The entity type ‘CartItem’ requires a primary key to be defined.

[The entity type "CartItem" needs to define a primary key.]

对,CartItem没有主键, 由于它是多对多关系,因此它应该具有复合主键。复合主键类似于常规主键,但它由两个属性(列)而不是一个属性组成。目前,创建复合键的唯一方法是在OnModelCreating.

protected override void OnModelCreating(ModelBuilder builder)
{
    base.OnModelCreating(builder);

    builder.Entity<CartItem>().HasKey(i => new { i.CartId, i.ItemId });
}

Finally, our database structure can be handled by the Entity Framework and we can continue migrating.

Insert many-to-many

假设我们已经有CartItem在我们的数据库中,现在我们想将特定商品(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();

Retrieving relevant data in many-to-many

从数据库中获取数据相当简单,注意使用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);

In addition, some operations can be performed without using relationships. For example, if you have a shopping cart ID, you can use the following Linq to get all items at once:

var cartId = 1;
var cartItems = db.Items.Where(item => item.Carts.Any(j => j.CartId == cartId));

The same principles apply to opposite use cases, which means that you can apply the pattern above to get all shopping carts with a specific item.

Delete from many-to-many

删除是指删除购物车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();

Then let me show you how to remove all items from your shopping cart.

var cart = db.Carts.Include(c=> c.Items).First(i => i.Id == 2);

db.RemoveRange(cart.Items);
db.SaveChanges();

Original author: Zbigniew

Original title: How to handle Many-To-Many in Entity Framework Core

Original link: softdevpractice.com/blog/many-to-many-ef-core/

Translation: The wolf at the end of the desert

Keep Exploring

延伸阅读

更多文章