How can I directly fill join table generated by EF Core for many-to-many relationship?

Viewed 170

I have these entities:

public class Book
{
  public int Id { get; set; }
  public string Title { get; set; }
  public List<Genre> Genres { get; set; }
}

public class Genre
{
  public int Id { get; set; }
  public string Name { get; set; }
  public List<Book> Books { get; set; }
}

EF Core 5 can handle creating join table but how can I use this table and should I do it? For example, I'm sending response to add two existing genres to existing book (I have book and genres ids). I suppose it would be better to directly fill join table with this ids instead of first getting those entities from context and then adding Genre entities to Genres field of Book entity.

1 Answers

If you look at docs and other docs, you can see that you can access the navigation, but It's pointed out in the docs that this may be changed later. Do not depend on the join type being Dictionary<string, object> unless this has been explicitly configured.

So I'd create a JoinTable by hand, it's not that hard really:

public class Book
{
  public int Id { get; set; }
  public string Title { get; set; }
  public List<BookGenre> BookGenres { get; set; }
}

public class Genre
{
  public int Id { get; set; }
  public string Name { get; set; }
  public List<BookGenre> BookGenres { get; set; }
}

public class BookGenre
{
  public int GenreId { get; set; }
  public int BookId { get; set; }

  public Genre Genre { get; set; }
  public Book Book { get; set; }
}

If you feel empowered you can add Fluent-api configurations so you depend on EF as little as possible. In the OnModelCreatingMethod in your context or in your separate Configuration file add (optional):

builder.Entity<BookGenre>()
  .ToTable("BookGenre")
  .HasKey(_ => new { _.BookId, _.GenreId });

builder.Entity<BookGenre>()
  .Property(_ => _.BookId)
  .ValueGeneratedNever();

builder.Entity<BookGenre>()
  .Property(_ => _.GenreId)
  .ValueGeneratedNever();

builder.Entity<BookGenre>()
  .HasOne(_ => _.Book)
  .WithMany(_ => _.BookGenres)
  .HasForeignKey(_ => _.BookId);

builder.Entity<BookGenre>()
  .HasOne(_ => _.Genre)
  .WithMany(_ => _.BookGenres)
  .HasForeignKey(_ => _.GenreId);

You also need to add the JoinTable to your Context:

public DbSet<BookGenre> BookGenreRelations { get; set; }

Now you can add new relations:

this.myContext.BookGenreRelations.Add(new BookGenre {
  BookId = myBookId,
  GenreId = myGenreId,
});

this.myContext.SaveChanges();

Note: In the above examples you can use async versions as well.

Related