Entity Framework Code First Tutorial Supplement: What is Going on in a Fluent API Call
In the new Entity Framework Code First MVC tutorial series there are some examples of what has come to be called fluent API method calls. This term refers to code in which a series of method calls are chained together. The Creating a More Complex Data Model tutorial briefly explains what this code does, but some readers of the tutorial expressed an interest in getting a more in-depth explanation, so I am providing that here, along with links to the relevant API reference documentation.
Here is the code block in question:
public class SchoolContext : DbContext
{
public DbSet<Course> Courses { get; set; }
public DbSet<Department> Departments { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Instructor> Instructors { get; set; }
public DbSet<Student> Students { get; set; }
public DbSet<OfficeAssignment> OfficeAssignments { get; set; }
protected override void OnModelCreating
(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Instructor>()
.HasOptional(p => p.OfficeAssignment)
.WithRequired(p => p.Instructor);
modelBuilder.Entity<Course>()
.HasMany(c => c.Instructors)
.WithMany(i => i.Courses)
.Map(t => t.MapLeftKey("CourseID")
.MapRightKey("InstructorID")
.ToTable("CourseInstructor"));
modelBuilder.Entity<Department>()
.HasOptional(x => x.Administrator);
}
}
HasOptional and WithRequired
The first of the mapping API method calls specifies a one-to-zero-or-one relationship between the Instructor
and OfficeAssignment
entities:
modelBuilder.Entity<Instructor>() .HasOptional(p => p.OfficeAssignment).WithRequired(p => p.Instructor);
Here is what's happening in this code:
- The DbModelBuilder.Entity method returns an EntityTypeConfiguration instance for the
Instructor entity
. - The EntityTypeConfiguration.HasOptional method returns an OptionalNavigationPropertyConfiguration instance for the
Instructor.OfficeAssignment
navigation property that specifies that theInstructor
entity may optionally have a relatedOfficeAssignment
entity. - The OptionalNavigationProperty.WithRequired method specifies that an
OfficeAssignment
entity must have a correspondingInstructor
entity. (The method returns a ForeignKeyNavigationPropertyConfiguration instance that you could use for further configuration, but no more is required for this relationship.)
HasMany and WithMany
The second statement specifies the table and column names for the join table of the many-to-many relationship between the Instructor
and Course
entities. Code First can configure the many-to-many relationship for you without this code, but if you don't call it, you will get default names such as InstructorInstructorID
for the InstructorID
column.
modelBuilder.Entity<Course>() .HasMany(c => c.Instructors).WithMany(i => i.Courses) .Map(t => t.MapLeftKey("CourseID") .MapRightKey("InstructorID") .ToTable("CourseInstructor"));
Here is what's happening in this code:
- The DbModelBuilder.Entity method returns an EntityTypeConfiguration instance for the
Course
entity. - The EntityTypeConfiguration.HasMany method returns a ManyNavigationPropertyConfiguration instance for the
Course.Instructors
navigation property that specifies that aCourse
entity may be related to manyInstructor
entities. - The ManyNavigationPropertyConfiguration.WithMany method returns a ManyToManyNavigationPropertyConfiguration instance that specifies that this is a many-to-many relationship and
Courses
is the corresponding navigation property in theInstructor
entity. - The ManyToManyNavigationPropertyConfiguration.Map method lets you configure the tables and columns used for this many-to-many relationship. It takes a ManyToManyAssociationMappingConfiguration instance in which you specify the column names by calling the MapLeftKey, MapRightKey, and ToTable methods. The "left" key is the one specified in the HasMany method; the "right" key is the one specified in the WithMany method.
HasOptional
In the third statement, the HasOptional statement specifies a one-to-zero-or-one relationship between the Department
and Instructor
tables, represented by the Department.Administrator
navigation property:
modelBuilder.Entity<Department>() .HasOptional(x => x.Administrator);
This code is identical to the one for the Instructor
-to-OfficeAssignment
relationship, except that in the Department
-to-Instructor
relationship, an Instructor
entity does not have to have a corresponding Department
, so the WithRequired
method is not called.
-- Tom Dykstra
ASP.NET Developer Guidance