EF Core – Many to Many

前言

Many to many 是 EF Core 5.0 才开始有的, 以前都用 2 个 1-n 来实现的.

由于它比 1-n 复杂, 所以有必要写一遍来记入一下.

参考:

Relationships

Property bag entity types

Without Fluent API

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public ICollection<Tag> Tags { get; set; }
}

public class Tag
{
    public string TagId { get; set; }

    public ICollection<Post> Posts { get; set; }
}

在完全没有 Fluent API 配置下, 只要 2 个 class 有关系的 collection 就可以表达 n-n 了.

它会自动创建一个 Join entity type configuration, 就可以生产 migration 了. 

为什么是 CategoryProduct 而不是 ProductCategory 呢? 

它是按 class name A-Z 排序的. C 比 P 前面, 所以就是 CategoryProduct.

Joining relationships configuration (Dictionary)

想自己配置 Join entity type configuration 也是 ok 的.

modelBuilder.Entity<Category>()
    .HasMany(e => e.Products)
    .WithMany(e => e.Categories)
    .UsingEntity<Dictionary<string, object>>(
        j => j
            .HasOne<Product>()
            .WithMany()
            .HasForeignKey("ProductId")
            .OnDelete(DeleteBehavior.Cascade),
        j => j
            .HasOne<Category>()
            .WithMany()
            .HasForeignKey("CategoryId")
            .OnDelete(DeleteBehavior.Cascade),
        j =>
        {
            j.ToTable("CategoryProduct");
            j.Property<int>("Id").HasColumnName("CategoryProductId");
            j.HasKey("Id").IsClustered(false);
            j.HasIndex("ProductId", "CategoryId").IsUnique().IsClustered(true);

            j.HasAnnotation("AuditTrail", null);
            j.Property<string>("CreatedBy");
            j.Property<DateTimeOffset>("DateCreated");
        }
    );

第 3 个 parameter 是配置 Join Entity. 注意这里用的是 Dictionary 来表示 Join Entity 而不是 class, 这个做法比较适合那种在 application level 不会用到这个 join entity 的情况.

如果需要用到的话, 那么应该用下面这个方式.

Joining relationships configuration (Class)

public class CategoryProduct
{
    public int Id { get; set; }
    public int ProductId { get; set; }
    public Product Product { get; set; } = null!;
    public int CategoryId { get; set; }
    public Category Category { get; set; } = null!;
    public string CreatedBy { get; set; } = "";
    public DateTimeOffset DateCreated { get; set; }
}

没有太大的区别, 就是定义多一个 class 就 ok 了.

modelBuilder.Entity<Category>()
    .HasMany(e => e.Products)
    .WithMany(e => e.Categories)
    .UsingEntity<CategoryProduct>(
        j => j.HasOne(e => e.Product)
            .WithMany()
            .HasForeignKey(e => e.ProductId)
            .OnDelete(DeleteBehavior.Cascade),
        j => j.HasOne(e => e.Category)
            .WithMany()
            .HasForeignKey(e => e.CategoryId)
            .OnDelete(DeleteBehavior.Cascade),
        j =>
        {
            j.ToTable("CategoryProduct");
            j.HasKey(e => e.Id).IsClustered(false);
            j.HasIndex(e => new { e.CategoryId, e.ProductId }).IsUnique().IsClustered(true);
        }
    );
原文地址:https://www.cnblogs.com/keatkeat/p/15685162.html