This is a simple and a very fundamental question to EF Core many-to-many relationship; I do not know why no one has written a complete example for n..m in EF Core.
I have modified your code (primary key as int
), I do not like string in primary key. Just copy/paste the code and every should work fine.
What entity should I add first? Books or maybe Library? How can I do this saving?
The order is not important the important thing here is the data linking. The data must be correctly linked, see the comments between my code lines.
Notes:
Many-to-many relationships without an entity class to represent the join table are not yet supported! You must have a join table.
Many-to-many relationships consists of 2 separate one-to-many relationships. = 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();
}
}
}
Results
Tables: Books | Libraries | Library2Book |
1 | 1 | 1 | 1 |
2 | - | 1 | 2 |
Edit from author of the question
When you're trying to insert a lot of entities (I've tried approximately 300), you'll have the same key has been already added error. You should split your inserting collection into small parts, e.g. 100 entities should be enough.
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();
}
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…