EF Feature CTP5 – Code First Fluent API

December 8, 2010

8 comments

EF Feature CTP5 – Code First Fluent API

One of Code First main features is the Fluent API. EF Feature CTP5 – Code First Fluent APIThis API can help you to configure the model in order to shape it (and the database) better. In this post I’m going to show a simple example for how to use the Fluent API. Pay attention that the details I provide might change in the future since its only a CTP and not a release.

The Example Model

In the example I’m going to use the following model:

public class SchoolEntities : DbContext
{
  #region Properties
 
  public DbSet<Course> Courses { get; set; }
  public DbSet<Department> Departments { get; set; }
 
  #endregion
 
  #region Methods
 
  protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
  {
    base.OnModelCreating(modelBuilder);
 
    // put here the fluent code
  }
 
  #endregion
}
 
public partial class Course
{
  #region Properties
 
  public int CourseID { get; set; }
  public string Title { get; set; }
  public string Days { get; set; }
  public DateTime Time { get; set; }
  public string Location { get; set; }
  public int Credits { get; set; }
  public int DepartmentID { get; set; }
 
  public virtual Department Department { get; set; }
 
  #endregion
}
 
public class Department
{
  #region Properties
 
  public int DepartmentID { get; set; }
  public string Name { get; set; }
  public decimal Budget { get; set; }
  public DateTime StartDate { get; set; }
  public int Administrator { get; set; }
 
  public virtual ICollection<Course> Courses { get; set; }
 
  #endregion
}

Fluent API Example

After you create your model there are a lot of ways to configure the model using the Fluent API. The main place to do that is in the OnModelCreating method which you override in the DbContext. That method gets a ModelBuilder instance which can be used to configure the model. The following example shows how to write some configuration with the API:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
  base.OnModelCreating(modelBuilder);
 
  modelBuilder.Entity<Department>().
    Property(d => d.Name).
    IsRequired().
    HasMaxLength(50);
 
  modelBuilder.Entity<Department>().
    Property(d => d.DepartmentID).
    HasDatabaseGenerationOption(DatabaseGenerationOption.None);
 
  modelBuilder.Entity<Department>().
    HasMany(d => d.Courses).
    WithRequired(c => c.Department).
    HasForeignKey(c => c.DepartmentID).
    WillCascadeOnDelete();
 
  modelBuilder.Entity<Department>().
    Ignore(d => d.Administrator);
 
  modelBuilder.Entity<Course>().
    Property(c => c.Title).
    IsRequired().
    HasColumnName("Name");      
}

What is Configured?

Lets explain what you see in the above code. When I want to configure an entity I use the Entity method with the entity as a generic parameter. Then I’m exposed to the Fluent API and can start configure my model.

In the first line I configure the Name property of the department as required and with length of no more than 50 characters.

In the second line I configure the DepartmentID as non database generated (since by default all the ID will be database generated).

The third line creates a one to many relation between the department and its course collection. You first use the HasMany to indicate the many side and then use the WithRequired to indicate the one side of the relation. The HasForeignKey indicate the foreign key on the many side. The last method, WillCascadeOnDelete, will add cascade delete on the entity graph.

The forth line will force the model to ignore the administrator property of the department and therefore it won’t be generated in the database.

The fifth line indicates that the Title property is required and that in the database the column name will be Name instead of Title.

Here is a simple example of creating data and inserting it to the database:

class Program
{
  static void Main(string[] args)
  {
    using (SchoolEntities context = new SchoolEntities())
    {
      var department = new Department
      {
        DepartmentID = 1,
        Administrator = 2,
        Budget = 100000,
        Name = "Data Access",
        StartDate = DateTime.Now
      };
      var course = new Course
      {
        Credits = 2,
        Days = "MF",
        Location = "Class 1",
        Time = DateTime.Now,
        Title = "Entity Framework",
        Department = department,
      };
      context.Departments.Add(department);
      context.SaveChanges();
    }
  }
}

Running this example will generate a new database by the name ClassLibrary1.SchoolEntities with all the configurations that were written in the OnModelCreating method. If you want to change the generated database name (which is taken from the namespace and context name by default) you need to create a constructor to the DbContext with a call for the base constructor that gets a string as parameter:

public SchoolEntities() :
      base("MySchool")
    {
 
    }

The resulting database:

Database

Summary

Lets sum up, Code First has a very interesting feature of fluent API. The fluent API for model configuration is easy to understand and use. In this post I showed and explain a small portion of the API.

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

8 comments

  1. John LegerDecember 13, 2010 ב 20:43

    Can you show me an example with code first where I can save the contents of an xml file which is UTF-8 encoded to a backing SqlXml db data type. I have upgraded to the EF CTP5 bits. Presently we are using a string and setting maxlength to a rather large value of 5000 to work around this. Any suggestions would be appreciated.

    Here is the class:

    public class DeviceCap
    {
    ///

    /// PK Id
    ///

    [Key]
    public int Id { get; set; }

    ///

    /// Gets or sets the device id.
    ///

    /// The JDF device id.
    [MaxLength(255)]
    [Required]
    public string DeviceId { get; set; }

    ///

    /// XMl containing the device capability information.
    ///

    [MaxLength(5000)]
    public SqlXml DeviceStream { get; set; }
    }

    When trying to wire this up here is the error
    (6,10) : error 3004: Problem in mapping fragments starting at line 6:No mapping specified for properties DeviceCap.DeviceStream in Set DeviceCaps.
    An Entity with Key (PK) will not round-trip when:
    Entity is type [CodeFirstNamespace.DeviceCap]

    Thanks Gil

    Reply
  2. Steven KimpeDecember 15, 2010 ב 22:34

    @John

    You can specify the sql datatype qsing the Column Attribute. see below.

       public class DeviceCap

       {

           /// <summary>

           /// PK Id

           /// </summary>

           [Key]

           public int Id { get; set; }

           /// <summary>

           /// Gets or sets the device id.

           /// </summary>

           /// <value>The JDF device id.</value>

           [MaxLength(255)]

           [Required]

           public string DeviceId { get; set; }

           /// <summary>

           /// XMl containing the device capability information.

           /// </summary>

           [Column(TypeName = "XML")]

           public string DeviceStream { get; set; }

       }

    Reply
  3. John SmithDecember 15, 2010 ב 23:30

    I am wondering if the LazyLoadingEnabled method works. I have code like the following:
    foreach (var d in db.Departments) {
    foreach (var c in d.Courses) {…}
    }
    and found the Courses are not loaded. Then I add a line
    db.Configuration.LazyLoadingEnabled = true;
    after the creation of the db object. Still does not work.
    The only way I can make it to work is to use the Include method as follows:
    foreach (var d in db.Departments.Include(“Courses”)) {
    foreach (var c in d.Courses) {…}
    }

    Reply
  4. Gil FinkDecember 16, 2010 ב 8:03

    @John Leger,

    Steve wrote an applicable answer – use the [Column(TypeName = "XML")] attribute.

    Reply
  5. Gil FinkDecember 16, 2010 ב 8:05

    @John Smith,

    If you didn’t added to the navigation properties the virtual keyword (which will enable EF to create runtime proxies with lazy loading) then it won’t work.

    Reply
  6. John SmithDecember 16, 2010 ב 19:17

    Thanks Gil, I have checked after adding the virtual keyword, my code works.

    Reply
  7. AubreyDecember 21, 2010 ב 23:42

    When I run this sample application, the course entity is not saved to the database. Only the Department is saves. Any idea why?

    Reply
  8. Gil FinkDecember 30, 2010 ב 8:45

    @Aubrey,
    Check that the Department property is virtual.

    Reply