Saturday, December 7, 2019

EFCore 3.0 Code First: Define Relations with Fluent API

Here is the summary of how EntityFrame Core 3.0 define entity relations with Fluent API.

  1.  1-0:1

    1 student to 0:1 StudentAddress.
  • Model classes:
    public class Student
    {
        public int Id { get; set; }
        public string LastName { get; set; }
        public string FirstMidName { get; set; }
        public StudentAddress Address { get; set; }
    }

    public class StudentAddress
    {
        public int StudentId { get; set; }
        public string AddressLine1 { get; set; }
        public string AddressLine2 { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string Zip { get; set; }
        public Student Student { get; set; }
    }
  • Mappings:
    public class StudentMap : IEntityTypeConfiguration<Student>
    {
        public void Configure(EntityTypeBuilder<Student> builder)
        {
            builder.ToTable(nameof(Student));
            builder.HasKey(x => x.Id);
        }
    }

    public class StudentAddressMap : IEntityTypeConfiguration<StudentAddress>
    {
        public void Configure(EntityTypeBuilder<StudentAddress> builder)
        {
            builder.ToTable(nameof(StudentAddress));
            builder.HasKey(u => u.StudentId);  // StudentAddress.StudentId is primary key
            builder.HasOne(u => u.Student)
                   .WithOne(b => b.Address)
                   .HasForeignKey<StudentAddress>(b => b.StudentId); // StudentAddress.StudentId is foreign key to Student.Id, too.
        }
    }

 public class SchoolContext : DbContext
    {
        public SchoolContext (DbContextOptions<SchoolContext> options)
            : base(options)
        {
        }

        public DbSet<Student> Students { get; set; }
        public DbSet<StudentAddress> StudentAddresses { get; set; }
     
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.ApplyConfiguration(new StudentMap());
            modelBuilder.ApplyConfiguration(new StudentAddressMap());
        }
    }
  • Generated tables:
CREATE TABLE [Student] (
    [Id] int NOT NULL IDENTITY,
    [LastName] nvarchar(max) NULL,
    [FirstMidName] nvarchar(max) NULL,
    CONSTRAINT [PK_Student] PRIMARY KEY ([Id])
);
CREATE TABLE [StudentAddress] (
    [StudentId] int NOT NULL,
    [AddressLine1] nvarchar(max) NULL,
    [AddressLine2] nvarchar(max) NULL,
    [City] nvarchar(max) NULL,
    [State] nvarchar(max) NULL,
    [Zip] nvarchar(max) NULL,
    CONSTRAINT [PK_StudentAddress] PRIMARY KEY ([StudentId]),
    CONSTRAINT [FK_StudentAddress_Student_StudentId] FOREIGN KEY ([StudentId]) REFERENCES [Student] ([Id]) ON DELETE CASCADE
);

2. 0:1-m

  1. 1 Auther to 1:m Book.

  2. public class SampleContext : DbContext
  3. {
  4. public DbSet<Book> Books { get; set; }
  5. protected override void OnModelCreating(ModelBuilder modelBuilder)
  6. {
  7. modelBuilder.Entity<Author>()
  8. .HasMany(a => a.Books)
  9. .WithOne(b => b.Author);
  10. }
  11. }

  12. public class Author
  13. {
  14. public int AuthorId { get; set; }
  15. public string FirstName { get; set; }
  16. public string LastName { get; set; }
  17. public ICollection<Book> Books { get; set; }
  18. }

  19. public class Book
  20. {
  21. public int BookId { get; set; }
  22. public string Title { get; set; }
  23. public Author Author { get; set; }
  24. }

3. 1-1:m

  1. 1 Auther to 0:m Book.

Same as 1-0:m except IsRequired() is added in relation definition as below:

  1. public class SampleContext : DbContext
  2. {
  3. public DbSet<Book> Books { get; set; }
  4. protected override void OnModelCreating(ModelBuilder modelBuilder)
  5. {
  6. modelBuilder.Entity<Author>()
  7. .HasMany(a => a.Books)
  8. .WithOne(b => b.Author)
  9. .IsRequired();
  10. }
  11. }