Thursday, October 3, 2019

EF6 Code-First Conventions


1. Default Convention for Table, Primary Key, Foreign Key & Column

Entity Framework code-first conventions

2. Convention 1 (0:1-m relationship)

EF 6 infers the 0:1-n relationship using the navigation property by default convention.

Relationship: Grade(0:1)-Student(many)
Parent class: Grade
Children class: Student


public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
}
The Student.Grade property is reference navigation property of Grade class. So, there can be many students in a single grade. This will result in a one-to-many relationship between the Students and Grades table in the database, where the Students table includes foreign key Grade_GradeId .

Notices:
  • The reference property Student.Grade is nullable, so it creates a nullable foreign key column Grade_GradeId in the Students table. This is called as optional one-to-many relationship, too.
These codes below falls into 0:1-m relation as above, too:
public class Student
{
    public int StudentId { get; set; }
    public string StudentName { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }

    public ICollection<Student> Students { get; set; } 
}
or

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeID { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
    
    public ICollection<Student> Students { get; set; }
}

3. Convention 2 (1-m relationship)

Relationship: Grade(1)-Student(many)
Parent class: Grade
Children class: Student



modelBuilder.Entity<Student>()
    .HasRequired<Grade>(s => s.Grade)
    .WithMany(g => g.Students);
or start configuring the relationship with Grade class:
modelBuilder.Entity<Grade>()
    .HasMany<Student>(g => g.Students)
    .WithRequired(s => s.Grade);


  • Method 2: Add non-nullable foreign key property GradeId with its reference property Grade in Student class as below:

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    
    public int GradeId { get; set; } //non-nullable foreign key property
    public Grade Grade { get; set; } //reference property
}

4. Convention 3 (m-m relationship)

Relationship: Student(many)-Course(many)

  • Method 1: by default

public class Student
{
    public Student() 
    {
        this.Courses = new HashSet<Course>();
    }

    public int StudentId { get; set; }
    [Required]
    public string StudentName { get; set; }

    public virtual ICollection<Course> Courses { get; set; }
}
        
public class Course
{
    public Course()
    {
        this.Students = new HashSet<Student>();
    }

    public int CourseId { get; set; }
    public string CourseName { get; set; }

    public virtual ICollection<Student> Students { get; set; }
}
  • Method 2: Fluent API

modelBuilder.Entity<Student>()
                .HasMany<Course>(s => s.Courses)
                .WithMany(c => c.Students);
EF will create a joining table StudentCourses in database for above example.

5. Convention 4 (1-0:1 relationship)

Relationship: Student(1)-StudentAddress(0:1)
  • Method 1: Using data annotation attributes

public class Student
{
    public int StudentId { get; set; }
    public string StudentName { get; set; }

    public virtual StudentAddress Address { get; set; }
}
     
public class StudentAddress 
{
    [ForeignKey("Student")]
    public int StudentAddressId { get; set; } //It is PK and FK
        
    public string Address1 { get; set; }
    public string Address2 { get; set; }
    public string City { get; set; }
    public int Zipcode { get; set; }
    public string State { get; set; }
    public string Country { get; set; }

    public virtual Student Student { get; set; }
}
Method 2: Fluent API
   // Configure Student & StudentAddress entity
    modelBuilder.Entity<Student>()
                .HasOptional(s => s.Address) // Mark Address property optional in Student entity
                .WithRequired(ad => ad.Student); // mark Student property as required in StudentAddress entity. Cannot save StudentAddress without Student
one-to-one relationship in code first

6. Convention 6 (1-1 relationship)

Relationship: Student(1)-StudentAddress(1)
  • Method: Using Fluent API where both ends are required
    // Configure StudentId as FK for StudentAddress
    modelBuilder.Entity<Student>()
                .HasRequired(s => s.Address) 
                .WithRequiredPrincipal(ad => ad.Student); 

7. Configure properties with Fluent API

    //Configure Null Column
    modelBuilder.Entity<Student>()
                .Property(p => p.Heigth)
                .IsOptional();
                        
    //Configure NotNull Column
    modelBuilder.Entity<Student>()
                .Property(p => p.Weight)
                .IsRequired();

    //Set StudentName column size to 50
    modelBuilder.Entity<Student>()
                .Property(p => p.StudentName)
                .HasMaxLength(50);

    //Set byte[] type property as a concurrency column
    modelBuilder.Entity<Student>()
                .Property(p => p.StudentName)
                .IsRowVersion();
8. EF Power Tools

Entity Framework 6 Power Tools are design-time utilities used in Visual Studio when working with the code-first development approach. It can create read-only entity data model.

    Download and install EF 6 Power Tools for Visual Studio 2015/2017 from Visual Studio Marketplace.