繁体   English   中英

在实体框架核心 6 中进行多对多实体关系更新的正确方法是什么?

[英]What is the correct way to do many to many entity relation update in Entity framework core 6?

 public class Book
 {
     public int Id { get; set; }
     public string Name{ get; set; }
     public string ISBN { get; set; }
    
     public ICollection<Category> Categories { get; set; }
 }
    
 public class Category
 {
     public int Id { get; set; }
     public string Name{ get; set; }
     public ICollection<Book> Books { get; set; }
 }

我使用 Entity Framework core 6 和 .NET 6。我正在尝试更新特定书籍的类别。

例如,如果一本书的类别有.NETC# ,那么我想将类别更新为.NETEF CoreSqlServer ,我想你现在明白了。

我是否需要仅为 Update 操作添加 Join 实体? 如您所见,尽管我在第一次创建 Book 时设法插入类别,但我还没有创建任何 Join 实体,例如BookCategories 但是当试图用新的类别更新这本书时,我遇到了两个问题。

  1. 旧类别不会被删除。
  2. 并在尝试使用现有类别更新时获取Duplicate Error Key ,在本例中为.NET

请展示在 .NET6 中更新 Entity Framework Core 6 中相关实体的正确方法。

多对多关系需要一些配置,具体取决于您希望从关系中得到什么。 如果您只想让链接表管理链接而不是其他任何东西:

[BookCategories]
BookId (PK, FK)
CategoryId (PK, FK)

然后,您可以设置关系以使用实体定义或影子实体。 在这两种情况下,这通常是更可取的,因为您的 Book 可以有一个 Categories 的集合,而 Category 可以有一个书籍的集合。 借助 Code-First 和 Migrations,我相信 EF 可以并且会自动设置此链接表。 否则,您可以使用 OnModelCreating 或 EntityTypeConfiguration 来配置要用于关系的表和列。

这可以使用为 BookCategory 声明的实体来完成,也可以不使用:

有实体:

modelBuilder.Entity<Book>()
   .HasMany(x => x.Categories)
   .WithMany(x => Books);
   .UsingEntity<BookCategory>(
       l => l.HasOne<Book>().WithMany().HasForeignKey(x => x.BookId),
       r => r.HasOne<Category>().WithMany().HasForeignKey(x => x.CategoryId),
       j =>
       {
           j.HasKey("BookId", "CategoryId");
           j.ToTable("BookCategories"); 
       });

没有实体:(请参阅脚手架多对多关系 - https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-6.0/whatsnew

modelBuilder.Entity<Book>()
   .HasMany(x => x.Categories)
   .WithMany(x => Books);
   .UsingEntity<Dictionary<string, object>>(
       "BookCategories",
       l => l.HasOne<Book>().WithMany().HasForeignKey("BookId"),
       r => r.HasOne<Category>().WithMany().HasForeignKey("CategoryId"),
       j =>
       {
           j.HasKey("BookId", "CategoryId");
           j.ToTable("BookCategories"); 
       });

或者,如果联接表需要包含其他相关详细信息,例如,如果您正在使用软删除系统并且想要将已删除的关系标记为非活动而不是删除这些行,那么您必须采用使用 BookCategory 实体的间接关系其中 Book 具有 BookCategories 的集合,Category 也是如此。 (请参阅加入实体类型配置 - https://docs.microsoft.com/en-us/ef/core/modeling/relationships?tabs=fluent-api%2Cfluent-api-simple-key%2Csimple-key

建立关系后,将这些关系视为关联而不是数据副本非常重要。 这意味着您应该确保您的 collections 在构造时已初始化,并且永远不会重置。 您可以将项目添加到集合或从集合中删除项目,但您不应该有重置集合的代码。 (即没有代码可以执行book.Categories = new List<Category>()book.Categories = myUpdatedCategories等。)虽然 EF 正在跟踪实体,但它依靠代理来帮助跟踪更改以了解何时需要数据被添加、删除或更新。 这也意味着如果你想“改变”一本书的类别,这是一个删除和添加,而不是更新。

例如,要将一本书的类别从“Java”更改为“.Net”,您不想执行以下操作:

var book = context.Books.Include(x => x.Categories).Single(x => x.BookId == bookId);
var category = book.Categories.SingleOrDefault(x => x.CategoryName == "Java");
if (category != null)
    category.CategoryName = ".Net"; // or category.CategoryId = dotNetCategoryId;

这将尝试修改类别记录以更改其名称(可能不是有意的)或尝试更改其 PK。 (非法的)

相反,您想更改关联:

var dotNetCategory = context.Categories.Single(x => x.CategoryId == dotNetCategoryId);
var book = context.Books.Include(x => x.Categories).Single(x => x.BookId == bookId);
var category = book.Categories.SingleOrDefault(x => x.CategoryName == "Java");
if (category != null)
{
    book.Categories.Remove(category);
    book.Categories.Add(dotNetCategory);
}

在幕后,EF 将删除将图书链接到 Java 类别的 BookCategory,并插入带有 new.Net 关联的 BookCategory。 如果您有一个加入实体,那么您只需要根据您想要进行的关系更改专门删除、添加或更新 BookCategory 实体。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM