在 Entity Framework Core 中保存多对多关系
Posted
技术标签:
【中文标题】在 Entity Framework Core 中保存多对多关系【英文标题】:Saving many-to-many relationship in Entity Framework Core 【发布时间】:2016-12-18 01:06:00 【问题描述】:例如,我有 3 个类,用于多对多关系:
public class Library
[Key]
public string LibraryId get; set;
public List<Library2Book> Library2Books get; set;
public class Book
[Key]
public string BookId get; set;
public List<Library2Book> Library2Books get; set;
public class Library2Book
public string BookId get; set;
public Book Book get; set;
public string LibraryId get; set;
public Library Library get; set;
它们在ApplicationDbContext
中配置:
protected override void OnModelCreating(ModelBuilder builder)
base.OnModelCreating(builder);
builder.Entity<CodeableConcept2Coding>().HasKey(k => new k.LibraryId, k.BookId );
builder.Entity<Library2Book>()
.HasOne(x => x.Library)
.WithMany(x => x.Library2Book)
.HasForeignKey(x => x.LibraryId);
builder.Entity<Library2Book>()
.HasOne(x => x.Book)
.WithMany(x => x.Library2Book)
.HasForeignKey(x => x.BookId);
所以,我想在数据库中添加一些Library2Books
的列表:
var library2Books = new List<Library2Books>(/*some sort of initialization*/);
我应该先添加什么实体? Books
或者 Library
?我怎样才能做到这一点?
【问题讨论】:
【参考方案1】:这是一个关于 EF Core 多对多关系的简单且非常基础的问题;我不知道为什么没有人在 EF Core 中为 n..m 编写完整的示例。
我修改了你的代码(主键为int
),我不喜欢主键中的字符串。只需复制/粘贴代码,每个都可以正常工作。
我应该先添加什么实体?书籍或图书馆?我怎样才能做到这一点?
顺序不重要,重要的是数据链接。数据必须正确链接,查看我的代码行之间的 cmets。
注意事项:
尚不支持没有实体类来表示连接表的多对多关系!您必须有一个连接表。
多对多关系由 2 个独立的一对多关系组成。 = 2x 1:N
class Program
public class Library
[Key]
public int LibraryId get; set;
public List<Library2Book> Library2Books get; set; = new List<Library2Book>();
public class Book
[Key]
public int BookId get; set;
public List<Library2Book> Library2Books get; set; = new List<Library2Book>();
public class Library2Book
[Key]
public int BookId get; set;
public Book Book get; set;
[Key]
public int LibraryId get; set;
public Library Library get; set;
public class MyDbContext : DbContext
public DbSet<Book> Books get; set;
public DbSet<Library> Libraries get; set;
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
optionsBuilder.UseSqlServer(@"Server=.\;Database=EFTutorial;integrated security=True;");
base.OnConfiguring(optionsBuilder);
protected override void OnModelCreating(ModelBuilder modelBuilder)
modelBuilder.Entity<Library2Book>().HasKey(k => new k.LibraryId, k.BookId );
modelBuilder.Entity<Library2Book>()
.HasOne(x => x.Book)
.WithMany(x => x.Library2Books)
.HasForeignKey(x => x.BookId);
modelBuilder.Entity<Library2Book>()
.HasOne(x => x.Library)
.WithMany(x => x.Library2Books)
.HasForeignKey(x => x.LibraryId);
base.OnModelCreating(modelBuilder);
static void Main(string[] args)
using (var myDb = new MyDbContext())
// Create Db
myDb.Database.EnsureCreated();
// I will add two books to one library
var book1 = new Book();
var book2 = new Book();
// I create the library
var lib = new Library();
// I create two Library2Book which I need them
// To map between the books and the library
var b2lib1 = new Library2Book();
var b2lib2 = new Library2Book();
// Mapping the first book to the library.
// Changed b2lib2.Library to b2lib1.Library
b2lib1.Book = book1;
b2lib1.Library = lib;
// I map the second book to the library.
b2lib2.Book = book2;
b2lib2.Library = lib;
// Linking the books (Library2Book table) to the library
lib.Library2Books.Add(b2lib1);
lib.Library2Books.Add(b2lib2);
// Adding the data to the DbContext.
myDb.Libraries.Add(lib);
myDb.Books.Add(book1);
myDb.Books.Add(book2);
// Save the changes and everything should be working!
myDb.SaveChanges();
结果
Tables: Books | Libraries | Library2Book |
1 | 1 | 1 | 1 |
2 | - | 1 | 2 |
问题作者编辑
当您尝试插入大量实体时(我尝试了大约 300 个),您将遇到已添加相同的键错误。您应该将插入集合分成小部分,例如100 个实体应该足够了。
public async Task SaveEntities(IEnumerable<Library2Book> library2Books)
int i = 0;
foreach (var library2Book in library2Books)
_dbContext.Set<Library>().Add(codConc2Coding.Library);
_dbContext.Set<Book>().Add(codConc2Coding.Book);
_dbContext.Set<Library2Book>().Add(library2Book);
i++;
if (i == 99)
await _dbContext.SaveChangesAsync();
i = 0;
await _dbContext.SaveChangesAsync();
【讨论】:
感谢您的回答!但我有疑问。澄清一些事情。所以,正如你所说,如果顺序不重要,如果我有var library2Books = new List<Library2Books>()
,我可以像这样将它添加到数据库中:myDb.Libraries.Add(library2Books.Select(x => x.Library))
,然后是myDb.Books.Add(library2Books.Select(x => x.Book))
,最后是myDb.Library2Books.Add(library2Books)
?
@YuriyN。不,这不起作用,您将获得至少 AddRange 而不是 Add 所需的转换
当然,错了,我们需要使用AddRange
,但我不能完全理解,为什么它不起作用。
@YuriyN。我认为 library2Books 的问题。它没有正确链接或映射到相关实体。这正是我试图用我的答案告诉你的。您能否转储您的实体添加/修改的实体的对象树。这是一个很好的方法,可以让我看到您的实体之间缺少什么或究竟是什么问题。
看起来问题可能是尝试异步保存的问题,或者您使用某种顺序键的事实,如果您使用 GUID,您可能会侥幸逃脱异步保存AND 没有得到重复键错误以上是关于在 Entity Framework Core 中保存多对多关系的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Entity Framework Core 中运行存储过程?
如何在 Entity Framework Core 中运行存储过程?
什么是自有实体?何时以及为什么在 Entity Framework Core 中使用 Owned Entity?
Entity Framework Core 中 TimeSpan 的总和
Entity Framework优化一:引发了“System.Data.Entity.Core.EntityCommandExecutionException”类型的异常