EF CTP4 Tips & Tricks: Include with Lambda

The release of Entity Framework Feature CTP4 was recently announced and included some walkthroughs touching on the core functionality included in the CTP. Over the next couple of weeks I’m going to post up a series of short articles that cover some of the finer points in the CTP.

The CTP includes some work called the Productivity Improvements for EF which aims to provide a simpler and more productive experience writing data access code with EF. Along with a bunch of conventions that take care of a lot of common tasks the Productivity Improvements also include some more subtle improvements over the core EF API. One of these is the introduction of an Include method that uses a lambda rather than strings to specify the include path.

Building a Quick Model

With the productivity improvements I can define a model using a set of POCO classes:

public class Blog 
{ 
    public int BlogId { get; set; } 
    public string Title { get; set; }

    public ICollection<Post> Posts { get; set; } 
}

public class Post 
{ 
    public int PostId { get; set; } 
    public string Title { get; set; } 
    public string Content { get; set; }

    public Blog Blog { get; set; } 
    public ICollection<Comment> Comments { get; set; } 
}

public class Comment 
{ 
    public int CommentId { get; set; } 
    public string CommentText { get; set; }

    public Post Post { get; set; } 
}

With the classes defined I just need to write a simple context:

public class BlogContext : DbContext 
{ 
    public DbSet<Blog> Blogs { get; set; } 
    public DbSet<Post> Posts { get; set; } 
    public DbSet<Comment> Comments { get; set; } 
}

Then I can use it for data access and EF will take care of discovering my model and creating a database for me:

using (var context = new BlogContext()) 
{ 
    foreach (var blog in context.Blogs) 
    { 
        Console.WriteLine(blog.Title); 
    } 
}

Existing Include

Include is a method we use in queries to specify that we should load related entities as well as the main type we are querying for, i.e. I want to query for a Blog but I want to bring all the Comments back into memory at the same time.

In EF4 (.NET 4.0, Visual Studio 2010) Include accepts a string to specify the related properties that should be loaded:

var blogsWithPosts = context.Blogs.Include("Posts");

The main complaints with this are the lack of intellisense, lack of compile time checking and that using refactoring in Visual Studio to rename the Blog.Posts property won’t update the string. Include was also an instance method on ObjectSet<T> so it had to be the first call when chaining query methods and wasn’t available if you’re set was typed as IObjectSet or IQueryable.

Include with Lambda

CTP4 includes a version of Include that accepts a lambda to specify the property to include. Note that this version of Include is an extension method so you will need to add a using for the System.Data.Entity namespace.

var blogsWithPosts = context.Blogs.Include(b => b.Posts);

This version of Include is an extension method on IQueryable<T> so you can add it after chaining other query methods:

var blogsWithPosts = context.Blogs 
    .Where(b => b.Title.StartsWith("A")) 
    .OrderBy(b => b.Title) 
    .Include(b => b.Posts);

Obviously not all implementations of IQueryable support Include, the implementation in CTP4 is EF specific and supports ObjectSet<T>, ObjectQuery<T> and DbSet<T> based queries. If the underlying implementation of IQueryable is something else then the extension method checks for a string based Include method to call, if there isn’t one then it is effectively a no-op.

Multi-Level Includes

Including more than one level in a hierarchy is still possible with the lambda Include, for reference properties you just dot through the hierarchy:

var commentsWithPostAndBlog = context.Comments.Include(c => c.Post.Blog);

For collections you use the Select method:

var blogsWithPostsAndComments = context.Blogs.Include(b => b.Posts.Select(p => p.Comments));

Summary

EF Feature CTP4 includes a new Include extension method in the System.Data.Entity namespace that allows you to specify include paths with a lambda. The Include extension method works with the new DbSet<T> along with existing ObjectSet<T> and ObjectQuery<T> types. This approach to specifying includes gives a better intellisense experience along with property name refactoring in Visual Studio and compile time checking of the include path.