EF Feature CTP4 Walkthrough: Code First

 


The information in this post is out of date.

Visit msdn.com/data/ef for the latest information on current and past releases of EF.

For Code First to a New Database see https://msdn.com/data/jj193542

For Code First to an Existing Database see https://msdn.com/data/jj200620


 

Introduction

We recently announced the release of Entity Framework Feature Community Technology Preview 4 (CTP4) . Feature CTP4 contains a preview of new features that we are considering adding to the core framework in the future and would like to get community feedback on. Feature CTP4 builds on top of the existing Entity Framework 4 (EF4) functionality that shipped with .NET Framework 4.0 and Visual Studio 2010.

This post will provide a walkthrough of the fundamental components of the Entity Framework Code First feature. Our Productivity Improvement work, which is also included in CTP4, provides a streamlined Code First experience that reduces the amount of code you need to write by providing a façade on top of the building blocks described in this post. This experience is covered in our CTP4 Productivity Improvement Walkthrough.

If you need assistance with CTP4 we have an Entity Framework Pre-Release Forum.

1. Install EF CTP4

If you haven’t already done so then you need to install Entity Framework Feature CTP4.

2. Create the Application

To keep things simple we’re going to build up a basic console application that uses Code First to perform data access.

· Open Visual Studio 2010

· File -> New -> Project…

· Select “Windows” from the left menu and “Console Application”

· Enter “EF.CodeFirst.Walkthrough” as the name

· Select “OK”

3. Create the Model

Code First is all about describing our model using classes so we are going to start by building out a simple model in code. We want our model to be persistence ignorant (i.e. not have any dependencies on Entity Framework) so we’ll add our model in a separate project.

· Add a new project for the model

 o File -> Add -> New Project…

 o Select “Windows” from the left menu and “Class Library”

 o Enter “EF.CodeFirst.Walkthrough.Model” as the name

 o Select “OK”

 

· Right click on the model project and add a class called “Book” with the following implementation

using System;

namespace EF.CodeFirst.Walkthrough.Model

{

    public class Book

    {

        public string ISBN { get; set; }

        public string Title { get; set; }

        public DateTime FirstPublished { get; set; }

        public bool IsFiction { get; set; }

        public virtual Publisher Publisher { get; set; }

        public virtual Author Author { get; set; }

    }

}

 

· Right click on the model project and add a class called “Person” with the following implementation

 

namespace EF.CodeFirst.Walkthrough.Model

{

    public class Person

    {

        public int PersonId { get; set; }

        public string FirstName { get; set; }

        public string LastName { get; set; }

    }

}

 

· Right click on the model project and add a class called “Author” with the following implementation

using System.Collections.Generic;

namespace EF.CodeFirst.Walkthrough.Model

{

    public class Author : Person

    {

        public int AuthorId { get; set; }

   public virtual ICollection<Book> Books { get; set; }

    }

}

 

· Right click on the model project and add a class called “Publisher” with the following implementation

using System.Collections.Generic;

namespace EF.CodeFirst.Walkthrough.Model

{

    public class Publisher

    {

        public int PublisherId { get; set; }

        public string Name { get; set; }

        public virtual ICollection<Book> Books { get; set; }

    }

}

4. Model Builder & Fluent API

If you’ve used the previous CTPs of Code First then this is where things to start to look a little different. As the name suggests ModelBuilder is used to configure the model, ModelBuilder then produces an immutable DbModel that can be used to construct either ObjectContext or DbContext instances.

Once you have created a DbModel this should be cached and re-used throughout your application to avoid incurring the performance hit of model creation multiple times. If you opt for the Code First approach described in the Productivity Improvements walkthrough then DbContext takes care of this caching for you.

· First we need to reference our model from the main application

 o Right click on the console application project -> Add Reference…

 o Select the “Project” tab

 o Select the model project from the list

 o Click “OK”

 

· We also need to reference the CTP4 assembly and the core Entity Framework assembly

 o Right click on the console application project -> Add Reference…

 o Select the “.NET” tab

 o Select “Microsoft.Data.Entity.Ctp” from the list

 o Click “OK”

 o Repeat the above steps for “System.Data.Entity”

 

· We also need to add a few using statement to the top of Program.cs

using System.Data.Entity.ModelConfiguration;

using System.Data.SqlClient;

using EF.CodeFirst.Walkthrough.Model;

 

· Next add the following code to the Main method in Program.cs to configure your model

static void Main(string[] args)

{

    var builder = new ModelBuilder();

           

    builder.Entity<Book>().HasKey(b => b.ISBN);

    builder.Entity<Book>().Property(b => b.Title).IsRequired();

    builder.Entity<Book>().HasRequired(b => b.Author).WithMany(a => a.Books);

    builder.Entity<Person>();

    builder.Entity<Publisher>().Property(p => p.Name).IsRequired().HasMaxLength(50);

}

 

Note: In the final section we will cover creating a derived context that exposes sets for the types in your model. Once you have a derived context you can use the ModelBuilder.DiscoverEntitiesFromContext(Type contextType) method to have the types that are exposed in sets automatically registered for you. This avoids the need to manually register types that do not require any additional configuration, such as Person in the code above.

Why Introduce DbModel?

In previous CTPs ContextBuilder basically did the work of both ModelBuilder and DbModel, introducing a separate representation for the final model does add an extra step in the process. In the future DbModel will become more fundamental to EF in general as we look at other ways to build models beyond ModelBuilder. DbModel allow us to have a single representation for a model that can then be used to construct both ObjectContext and DbContext instances.

Conventions

You’ll notice there are a lot of things I’m not specifying about the model, for example that Person.PersonId is a primary key or that Book.Publisher and Publisher.Books are inverse navigation properties of the same relationship. This is because Code First now includes an extended set of conventions to take care of common configuration tasks for you. The conventions are discussed in detail in this Conventions design blog post, the conventions included in CTP4 are:

  • Primary Key
  • Relationship Inverse
  • Foreign Key
  • Pluralization of Table Names

 

Any configuration that you specify explicitly via the Fluent API will take precedence over what is discovered via convention.

Data Annotations

Code First will also now consume Data Annotations so rather than configuring Book.ISBN as the primary key via the Fluent API we could have added the Key attribute (System.ComponentModel.DataAnnotations.KeyAttribute) to the ISBN property.

Data Annotations are described in detail in this design blog post, the annotations supported in CTP4 are:

  • Key
  • StringLength
  • ConcurrencyCheck
  • Required
  • Timestamp
  • DataMember
  • RelatedTo
  • MaxLength
  • StoreGenerated

Data Annotations take precedence over conventions but explicit configuration via the Fluent API still has the highest precedence.

Relationship API Changes

If you’ve used the previous CTPs of Code First you’ll also notice we have changed the syntax for specifying relationships in an attempt to make it more intuitive. The single Relationship method has now been replaced with HasRequired, HasOptional and HasMany. Inverse navigations are then specified via WithRequired, WithOptional and WithMany. If you expose foreign key properties on your entities then these can be configured via the HasConstaint method.

The main benefits of the refactoring are:

  • Reduced number of calls required to configure a relationship
  •  API Signatures are more understandable
    • The single Relationship method with overloads for references and collections was proving to be confusing
  • Removal of invalid methods
    • The previous API exposed Required and Optional for ‘many’ ends of relationships
  • Ability to full specify relationships that only expose one navigation property

Using the one-to-many relationship between Book and Author as an example, this would previously have been configured as follows

builder.Entity<Book>().Relationship(b => b.Author).IsRequired().FromProperty(a => a.Books);

With the API changes this is now

 

builder.Entity<Book>().HasRequired(b => b.Author).WithMany(a => a.Books);

 

If the Author.Books property was not included in the model it is now possible to still completely specify the relationship

 

builder.Entity<Book>().HasRequired(b => b.Author).WithMany();

 

Or alternatively if the Book.Author property was not included

 

builder.Entity<Author>().HasMany(a => a.Books).WithRequired();

5. Configuration Classes

Now let’s say we want to encapsulate the configuration for Book in a separate class. We can do this by creating a derived configuration class for Book and then registering it with our model builder. This is useful if you want to re-use configurations within an application or even between applications. Configuration classes also nicely separate configuration related code from the rest of our application code.

· Right click on the console application project and add a class called “BookConfiguration” with the following implementation

using System.Data.Entity.ModelConfiguration;

using EF.CodeFirst.Walkthrough.Model;

namespace EF.CodeFirst.Walkthrough

{

    public class BookConfiguration : EntityConfiguration<Book>

    {

        public BookConfiguration()

        {

            this.HasKey(b => b.ISBN);

            this.Property(b => b.Title).IsRequired();

            this.HasRequired(b => b.Author).WithMany(a => a.Books);

        }

    }

}

· We can then modify the Main method in Program.cs to be as follows

static void Main(string[] args)

{

    var builder = new ModelBuilder();

    builder.Configurations.Add(new BookConfiguration());

    builder.Entity<Person>();

    builder.Entity<Publisher>().Property(p => p.Name).IsRequired().HasMaxLength(50);

}

6. Create a Derived Context

Now that we have a model configured the easiest way to interact with the classes is through a derived context. With the introduction of the Productivity Improvements that we mentioned earlier there are two options here. You can either derive from our existing ObjectContext type or the new alternative DbContext type, which has a simpler API surface. We’ll look at both options in this walkthrough.

6.1. Option 1: Object Context

· Right click on the console application project and add a class called “BookCatalog” with the following implementation

using System.Data.EntityClient;

using System.Data.Objects;

using EF.CodeFirst.Walkthrough.Model;

namespace EF.CodeFirst.Walkthrough

{

    public class BookCatalog : ObjectContext

    {

        public BookCatalog(EntityConnection connection)

            : base(connection)

        { }

        private ObjectSet<Book> _books;

        public ObjectSet<Book> Books

        {

            get

            {

                return this._books == null

                    ? this._books = this.CreateObjectSet<Book>()

                    : this._books;

            }

        }

        private ObjectSet<Person> _people;

        public ObjectSet<Person> People

        {

            get

      {

                return this._people == null

                    ? this._people = this.CreateObjectSet<Person>()

                    : this._people;

            }

        }

        private ObjectSet<Publisher> _publishers;

        public ObjectSet<Publisher> Publishers

        {

            get

            {

                return this._publishers == null

                    ? this._publishers = this.CreateObjectSet<Publisher>()

                    : this._publishers;

            }

        }

    }

}

 

· With a derived context defined we can now modify the Main method in Program.cs to use it for data access:

static void Main(string[] args)

{

    var builder = new ModelBuilder();

    builder.Configurations.Add(new BookConfiguration());

    builder.Entity<Person>();

    builder.Entity<Publisher>().Property(p => p.Name).IsRequired().HasMaxLength(50);

    var model = builder.CreateModel();

    using (var connection = new SqlConnection(@"Server=.\SQLEXPRESS;Database=CodeFirstWalkthrough;Trusted_Connection=True;"))

    {

        using (var context = model.CreateObjectContext<BookCatalog>(connection))

        {

            if (!context.DatabaseExists())

            {

                context.CreateDatabase();

            }

            var book = new Book

            {

                ISBN = "1111",

                Title = "Intro to Code First",

                FirstPublished = DateTime.Today,

                IsFiction = false,

       Author = new Author { FirstName = "Rowan", LastName = "Miller" },

                Publisher = new Publisher { Name = "EF Books" }

            };

            context.Books.AddObject(book);

            context.SaveChanges();

        }

    }

}

 

6.2. Option 2: DbContext

We’re going to look at constructing a DbContext from a DbModel but as previously mentioned there is simpler Code First experience available that avoids the need to externally create a ModelBuilder and DbModel and also takes care of model caching, this is described in the Productivity Improvements walkthrough.

· Right click on the console application project and add a class called “SimpleBookCatalog” with the following implementation:

using System.Data.Entity;

using EF.CodeFirst.Walkthrough.Model;

using System.Data.Entity.Infrastructure;

namespace EF.CodeFirst.Walkthrough

{

    public class SimpleBookCatalog : DbContext

    {

        public SimpleBookCatalog(DbModel model)

            : base(model)

        { }

        public DbSet<Book> Books { get; set; }

        public DbSet<Person> People { get; set; }

        public DbSet<Author> Authors { get; set; }

        public DbSet<Publisher> Publishers { get; set; }

    }

}

 

· With a derived context defined we can now modify the Main method in Program.cs to use it for data access
Note that DbContext takes care of creating a database for us so we don’t need to specify a connection, this convention can be overridden and is described in the Productivity Improvements walkthrough.

static void Main(string[] args)

{

    var builder = new ModelBuilder();

    builder.Configurations.Add(new BookConfiguration());

    builder.Entity<Person>();

    builder.Entity<Publisher>().Property(p => p.Name).IsRequired().HasMaxLength(50);

    var model = builder.CreateModel();

    using (var context = new SimpleBookCatalog(model))

    {

        var book = new Book

            {

                ISBN = "2222",

                Title = "Intro to Code First",

                FirstPublished = DateTime.Today,

                IsFiction = false,

                Author = new Author { FirstName = "Rowan", LastName = "Miller" },

                Publisher = new Publisher { Name = "EF Books" }

            };

  context.Books.Add(book);

            context.SaveChanges();

    }

}

7. Summary

In this walkthrough we looked at how the fundamental Code First components can be used to configure a model and create a context to perform data access. We saw the extended default conventions, Data Annotations and the updated relationship API that are new in CTP4.

As mentioned during the post these building blocks are also used to provide a simpler and more streamlined Code First experience in the Productivity Improvement work described in this post.

 

Rowan Miller
Program Manager
ADO.NET Entity Framework

EF.CodeFirst.Walkthrough.zip