写点什么

EF Core 如何处理多对多关系

作者:喵叔
  • 2021 年 11 月 23 日
  • 本文字数:1942 字

    阅读完需:约 6 分钟

EF Core 在处理多对多关系时并不像一对一和一对多关系那样好处理,下面我们利用一个简单的电子商城购物车来讲解一下吧。

一、解决多对多

需求是这样的:用户可以将多个商品放入购物车,每个商品又属于多个购物车。我们先创建 ShoppingCart 和 Commodity 实体类。


public class ShoppingCart{    public int Id { get; set; }    public ICollection<Commodity> Commoditys{ get; set; }}public class Commodity{    public int Id { get; set; }    public string Name { get; set; }    public int Quantity { get; set; }    public ICollection<ShoppingCart> ShoppingCarts{ get; set; }}
复制代码


你第一眼看到这段代码是不是觉得这么做非常好?但是我要告诉你的是,到目前为止 EF Core 无法处理这样的代码,当你尝试添加迁移时控制台会输出如下内容:


Unable to determine the relationship represented by navigation property 'ShoppingCart.Commoditys' of type 'ICollection'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
复制代码


那么我们该怎么做呢?聪明的同学一定想到了我们可以手动创建另一个中间表,它将建立 ShoppingCart 和 Commodity 多对多的关系。


public class ShoppingCartCommodity{    public int ShoppingCartId { get; set; }    public ShoppingCart ShoppingCart{ get; set; }    public int CommodityId { get; set; }    public Commodity Commodity{ get; set; }}
复制代码


创建完中间表 ShoppingCartCommodity,我们还要修改 ShoppingCart 和 Commodity 的导航属性:


public class ShoppingCart{    public int Id { get; set; }    public ICollection<ShoppingCartCommodity> Commoditys { get; set; }}public class Commodity{    public int Id { get; set; }    public string Name { get; set; }    public int Quantity { get; set; }    public ICollection<ShoppingCartCommodity> ShoppingCarts{ get; set; }}
复制代码


你以为这样处理完就完美了吗?NO!!!当你再次尝试添加迁移时会出现另一个错误提示:


The entity type 'ShoppingCart' requires a primary key to be defined.
复制代码


ShoppingCart 没有主键,由于多对多关系因此 ShoppingCart 应该是复合主键。复合主键由两列组成一个主键,在 EF Core 中创建复合键唯一办法是在 OnModelCreating 中创建。


protected override void OnModelCreating(ModelBuilder builder){    base.OnModelCreating(builder);    builder.Entity<ShoppingCartCommodity>().HasKey(p => new { p.ShoppingCartId, p.CommodityId});}
复制代码


到这里可以说才解决了 EF Core 处理多对多的问题。解决了多对多创建表的问题,下面我们就来看一下如何进行增删查。

二、增

我们要把商品添加到购物车中,我们需要创建 ShoppingCartCommodity 并保存它。


var shoppingCart= db.ShoppingCarts.First(i => i.Id == 1);var commodity= db.Commoditys.First(i => i.Id == 2);// 方法1:使用两个类的主键ID关联var shoppingCartCommodity1= new ShoppingCartCommodity{    ShoppingCartId = shoppingCart.Id,    CommodityId = commodity.Id};// 放法2:使用两个类实体关联var shoppingCartCommodity2= new ShoppingCartCommodity{    ShoppingCart= cart,    Commodity= item};db.Add(shoppingCartCommodity2);db.SaveChanges();
复制代码

三、查

从数据库中获取数据只需使用 Include 查询即可。


var shoppingCartIncludingCommoditys = db.Carts.Include(shoppingCart=> shoppingCart.Commoditys).ThenInclude(row => row.Commodity).First(shoppingCart=> shoppingCart.Id == 1);// 获取指定购物车的所有商品var shoppingCartCommodity2= shoppingCartIncludingCommoditys.Commoditys.Select(row => row.Commodity);// 如果有购物车ID,则可以使用Linq获取所有商品:var shoppingCartId = 1;var shoppingCartCommoditys= db.Commoditys.Where(commodity=> commodity.shoppingCart.Any(j => j.ShoppingCartId== shoppingCartId));
复制代码

四、删

如果要删除购物车中的商品时,可以这么做:


var shoppingCartId = 1;var commodityId= 1;var shoppingCartCommodity= db.ShoppingCartCommoditys.First(row => row.ShoppingCartId == shoppingCartId && row.CommodityId== commodityId);db.Remove(shoppingCartCommodity);db.SaveChanges();
复制代码


如果要从购物车中删除所有项目,可以这么做:


var shoppingCart= db.ShoppingCarts.Include(c=> c.Commodity).First(i => i.Id == 2);db.RemoveRange(shoppingCart.Commoditys);db.SaveChanges();
复制代码


发布于: 3 小时前阅读数: 5
用户头像

喵叔

关注

还未添加个人签名 2020.01.14 加入

还未添加个人简介

评论

发布
暂无评论
EF Core如何处理多对多关系