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:

  1. The DbModelBuilder.Entity method returns an EntityTypeConfiguration instance for the Instructor entity.
  2. The EntityTypeConfiguration.HasOptional method returns an OptionalNavigationPropertyConfiguration instance for the Instructor.OfficeAssignment navigation property that specifies that the Instructor entity may optionally have a related OfficeAssignment entity.
  3. The OptionalNavigationProperty.WithRequired method specifies that an OfficeAssignment entity must have a corresponding Instructor 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:

  1. The DbModelBuilder.Entity method returns an EntityTypeConfiguration instance for the Course entity.
  2. The EntityTypeConfiguration.HasMany method returns a ManyNavigationPropertyConfiguration instance for the Course.Instructors navigation property that specifies that a Course entity may be related to many Instructor entities.
  3. 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 the Instructor entity.
  4. 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

Comments

  • Anonymous
    October 23, 2011
    This shines a tiny light on the subject.  Where can I get a readable, full documenation of the Fluent API EF configuration?
  • Anonymous
    October 24, 2011
    See this MSDN page for fluent API documentation:msdn.microsoft.com/.../hh295847(VS.103).aspx
  • Anonymous
    October 31, 2012
    I'm not sure but in the third statement, Is the HasOptional method specifying a one-to-zero-or-one relationship? I'm following the "Creating a More Complex Data Model" tutorial and there's a zero-or-one-to-many relationship between the Instructor and Department tables.I'm sorry English isn't my main language ;).
  • Anonymous
    July 09, 2013
    Will the mapping works without this settings?
  • Anonymous
    July 09, 2013
    In most cases you can do the same mapping using annotations that you can do with fluent API.  There are a few exceptions that can only be done by using API.  For details, see this book:  shop.oreilly.com/.../0636920022220.do
  • Anonymous
    January 22, 2014
    Find the latest walkininterviews at www.walkininterviews.info
  • Anonymous
    February 02, 2015
    Whoever came up with this "Fluent" API is a moron. This is completely incomprehensible, and turns into a gigantic unintelligible mess in the DbContext. Avoid Fluent unless absolutely necessary. Use Attributes instead.
  • Anonymous
    March 24, 2015
    Thank you so much! I learned so much!