EF Coreの多対多関係をどう扱うか。

EF Coreの多対多関係をどう扱うか。

多対多リレーションシップは他のリレーションシップほど単純ではありません。この記事では、多対多リレーションシップの作成方法とEF Coreでの使用方法を紹介します。

最后更新 2021/11/02 20:47
Zbigniew
预计阅读 5 分钟
分类
EF Core
标签
.NET EF Core ORM

多対多リレーションシップは他のリレーションシップほど単純ではありません。この記事では、多対多リレーションシップの作成方法とEF Coreでの使用方法を紹介します。

モデルは

多对多的简单而实用的例子可能是某种数字电子商务商店。用户可以将商品放入购物车(一个购物车可以有多个商品),而商品属于多个购物车。让我们从创建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; }
}

それは良いように見えますが、うまくいきません。この記事の公開時点では、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を使用して無視します。

我们需要做的第一件事是手动创建另一个“中间”类(表),它将建立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; }
}

今すぐ移行を追加しようとすると、別のエラーが発生します。

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

最後に、データベース構造はEntity Frameworkで処理でき、移行を進めることができます。

挿入は多対多

假设我们已经有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();

多対多でのデータ取得

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

同じ原理は反対のユースケースにも適用されます。つまり、上記のパターンを適用して、特定のアイテムを持つすべてのショッピングカートを取得できます。

mult-to-mult-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();

次に、ショッピングカートからすべてのアイテムを削除する方法をご紹介します。

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

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

著者:ズビグニェフ

前の記事:Entity Framework Coreで多くのことを扱う方法

原文へのリンク:https//softdevpractice.com/blog/many-to-many-ef-core/

翻訳:砂漠の果ての狼

Keep Exploring

延伸阅读

更多文章
同分类 / 同标签 2022/06/02

EF Core 6の新機能概要(4)

この記事では、SQLite、インメモリプロバイダ、およびEF. Functions.Containsメソッドに対するEF Coreの改善点について説明します。

继续阅读
同分类 / 同标签 2022/06/02

EF Core 6の新機能概要(2)

前回の記事に続き、この記事では、バリューコンバータ、足場、Db Contextの改善など、EF Core 6の10の新機能を紹介します。

继续阅读