Monday, 27 July 2020

C# - Relationships in Entity Framework Core


One To One Relationships

public class Author
{
    public int AuthorId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public AuthorBiography Biography { get; set; }
}

public class AuthorBiography
{
    public int AuthorBiographyId { get; set; }
    public string Biography { get; set; }
    public DateTime DateOfBirth { get; set; }
    public string PlaceOfBirth { get; set; }
    public string Nationality { get; set; }
   public int AuthorRef { get; set; }
    public Author Author { get; set; }
}
It is configured using the Fluent API like this:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
   modelBuilder.Entity<Author>()
       .HasOne(a => a.Biography)
       .WithOne(b => b.Author)
        .HasForeignKey<AuthorBiography>(b => b.AuthorRef);
}

 

One To Many Relationships

public class Company
{
   public int Id { get; set; }
    public string Name { get; set; }
    public ICollection<Employee> Employees { get; set; }
}

public class Employee
{
   public int Id { get; set; }
    public string Name { get; set; }
    public Company Company { get; set; }
}
A company has many employees, each with one company.
protected override void OnModelCreating(Modelbuilder modelBuilder)
{
    modelBuilder.Entity<Company>()
        .HasMany(c => c.Employees)
        .WithOne(e => e.Company);
}
It can also be configured by starting with the other end of the relationship:
protected override void OnModelCreating(Modelbuilder modelBuilder)
{
    modelBuilder.Entity<Employee>()
        .HasOne(e => e.Company)
        .WithMany(c => c.Employees);
}

Many To Many Relationships

A many-to-many relationship is defined in code by the inclusion of collection properties in each of the entities - The Categories property in the Book class, and the Books property in the Category class:

public class Book
{
    public int BookId { get; set; }
    public string Title { get; set; }
    public Author Author { get; set; }
    public ICollection<Category> Categories { get; set; }
}  


public class Category
{
    public int CategoryId { get; set; }
    public string CategoryName { get; set; }
    public ICollection<Book> Books { get; set; }
}
Entity Framework, this model definition was sufficient for EF to imply the correct type of relationship and to generate the join table for it. In EF Core it is necessary to include an entity in the model to represent the join table, and then add navigation properties to either side of the many-to-many relations that point to the join entity instead:
public class Book
{
    public int BookId { get; set; }
    public string Title { get; set; }
    public Author Author { get; set; }
    public ICollection<BookCategory> BookCategories { get; set; }
}  

public class Category
{
    public int CategoryId { get; set; }
    public string CategoryName { get; set; }
    public ICollection<BookCategory> BookCategories { get; set; }
}  

public class BookCategory
{
   public int BookId { get; set; }
    public Book Book { get; set; }
   public int CategoryId { get; set; }
    public Category Category { get; set; }
}
The join table will be named after the join entity (BookCategory in this case) by convention. The relationship also needs to be configured via the Fluent API for EF Core to be able to map it successfully:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<BookCategory>()
        .HasKey(bc => new { bc.BookId, bc.CategoryId });  

   modelBuilder.Entity<BookCategory>()
        .HasOne(bc => bc.Book)
        .WithMany(b => b.BookCategories)
        .HasForeignKey(bc => bc.BookId);  

   modelBuilder.Entity<BookCategory>()
        .HasOne(bc => bc.Category)
        .WithMany(c => c.BookCategories)
       .HasForeignKey(bc => bc.CategoryId);
}
The primary key for the join table is a composite key comprising both of the foreign key values. In addition, both sides of the many-to-many relationship are configured using the HasOneWithMany and HasForeignKey Fluent API methods.
This is sufficient if you want to access book category data via the Book or Category entities. If you want to query BookCategory data directly, you should also add a DbSet for the join table to the context:
public class SampleContext : DbContext
{
    public DbSet<Book> Books { get; set; }
    public DbSet<Category> Categories { get; set; }
    public DbSet<BookCategory> BookCategories { get; set; }
}


No comments:

Post a Comment