Code First Mapping Changes in CTP5


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


Code First allows you to map your .NET objects to a database schema. This blog post describes how you can accomplish many common mapping tasks using data annotation attributes and/or the fluent API and compares how you would accomplish these tasks in Entity Framework Feature CTP4 to what is now available in CTP5.

Code First mapping is about two things: using conventions and overriding those conventions with data annotation attributes and the fluent API. By default, Code First will build a mapping from your .NET objects to a database schema using conventions. When you want to override this default mapping, you can use data annotations and the fluent API. Data annotation attributes are targeted at providing a mechanism to override the most common kinds of configuration, things like setting maximum length for a property, setting a table or column name, or designating which properties are keys. There are some kinds of configuration, however, that cannot be done with the built-in data annotations: things like specifying more advanced or specific mapping scenarios such as splitting a single entity into two tables (called entity splitting), configuring inheritance hierarchies, or splitting a single table into multiple entities (called table splitting).

There were two major changes to the mapping API between CTP4 and CTP5. The first is that we moved the ability to rename columns and tables to be closer to the thing you were renaming. So, for example, there is a ToTable method to name a table on the entity configuration, and a HasColumnName method on a property configuration to rename a column. The second major change was that we removed the MapSingleType/MapHierarchy methods and replaced them with a single method called Map that can call when you are configuring an entity and when you are configuring a relationship on an entity. The map method uses a new mapping configuration class that replaces a lot of the “anonymous type” parameters in CTP4 in favor of explicit method calls. This allowed us to remove some of the utility classes such as EntityMap which were used when you wanted to rename something to a name that couldn’t be expressed as a .NET name.

Below are a number of common scenarios with examples of what the code looked like in CTP4, and how you can accomplish the same in CTP5. In many cases, the CTP4 code will not work in CTP5 as methods have been removed in favor of different methods locations.

Renaming a Table

In CTP4, you could rename a table using the MapSingleType method like in this example below:

public class Product

{

    public int Id { get; set; }

    public string Name { get; set; }

    public Category Category { get; set; }

}

 

public class MyContext : DbContext

{

    public DbSet<Product> Products { get; set; }

 

    protected override void OnModelCreating(ModelBuilder modelBuilder)

    {

        // Rename a table

        modelBuilder.Entity<Product>()

            .MapSingleType()

            .ToTable("ProductsTbl");

    }

}

 

In CTP5, a new data annotation attribute, TableAttribute, has been added to allow you to rename a table using an attribute:

[Table("ProductsTbl")]

public class Product

{

    public int Id { get; set; }

    public string Name { get; set; }

    public Category Category { get; set; }

}

 

You can also rename a table using the fluent API. In CTP5, the MapSingleType method has been removed and a new ToTable method has been added directly on the EntityTypeConfiguration class.

public class Product

{

    public int Id { get; set; }

    public string Name { get; set; }

    public Category Category { get; set; }

}

 

public class MyContext : DbContext

{

    public DbSet<Product> Products { get; set; }

 

    protected override void OnModelCreating(ModelBuilder modelBuilder)

    {

        // Rename a table

        modelBuilder.Entity<Product>()

            .ToTable("ProductsTbl");

    }

}

 

Renaming a Column

In CTP4, you could rename a column by using the MapSingleType (or MapHierarchy) method and specifying the properties you wanted to map by passing an anonymous type. In the example below the Product.Name property is mapped to the “ProductName” column.

public class Product

{

    public int Id { get; set; }

    public string Name { get; set; }

    public Category Category { get; set; }

}

 

public class MyContext : DbContext

{

    public DbSet<Product> Products { get; set; }

 

    protected override void OnModelCreating(ModelBuilder modelBuilder)

    {

        // Rename a column

        modelBuilder.Entity<Product>()

            .MapSingleType(p => new {

                    p.Id,

                    ProductName = p.Name

                });

    }

}

 

In CTP5, a new data annotation attribute, ColumnAttribute, has been added to allow you to rename a column using an attribute:

public class Product

{

    public int Id { get; set; }

    [Column(Name="ProductName")]

    public string Name { get; set; }

    public Category Category { get; set; }

}

 

You can also rename a column using the fluent API and the HasColumnName method on the property configuration classes.

public class Product

{

    public int Id { get; set; }

    public string Name { get; set; }

    public Category Category { get; set; }

}

 

public class MyContext : DbContext

{

    public DbSet<Product> Products { get; set; }

 

    protected override void OnModelCreating(ModelBuilder modelBuilder)

    {

        // Rename a column

        modelBuilder.Entity<Product>()

            .Property(p => p.Name)

            .HasColumnName("ProductName");

    }

}

 

Table-per-hierarchy (TPH) inheritance

Table-per-hierarchy (TPH) is a strategy for mapping a complete .NET object type hierarchy to a single database table. This is the default inheritance mapping strategy for both CTP4 and CTP5. In addition to the data from all properties for the types in the hierarchy, the table also includes a special column called the discriminator column which determines which .NET type a particular row of data is. This was accomplished in CTP4 by using the MapHierarchy method where you specified which properties were mapped to each table and what the discriminator value should be:

public class Product

{

    public int Id { get; set; }

    public string Name { get; set; }

    public Category Category { get; set; }

}

 

public class DiscontionuedProduct : Product

{

    public DateTime DiscontinuedOn { get; set; }

}

 

public class MyContext : DbContext

{

    public DbSet<Product> Products { get; set; }

 

    protected override void OnModelCreating(ModelBuilder modelBuilder)

    {

        modelBuilder.Entity<Product>()

            .MapHierarchy()

            .Case<Product>(p => new {

                    p.Id,

  p.Name,

                    disc = "P"

                })

             .Case<DiscontionuedProduct>(d => new {

                    d.DiscontinuedOn,

                    disc = "D"

                });

    }

}

 

In CTP5, the MapHierarchy method was replaced with the method Map, which takes a mapping configuration class with options on it. To specify the name of the discriminator column or the values for the discriminator, you use the “Required” method on the mapping configuration class:

public class Product

{

    public int Id { get; set; }

    public string Name { get; set; }

    public Category Category { get; set; }

}

 

public class DiscontionuedProduct : Product

{

    public DateTime DiscontinuedOn { get; set; }

}

 

public class MyContext : DbContext

{

    public DbSet<Product> Products { get; set; }

 

protected override void OnModelCreating(ModelBuilder modelBuilder)

{

    modelBuilder.Entity<Product>()

        .Map(mc => {

                mc.Requires("disc").HasValue("P");

            })

        .Map<DiscontionuedProduct>(mc => {

                mc.Requires("disc").HasValue("D");

            });

       

}

 

Table-per type (TPT) inheritance

The Table-per-type (TPT) inheritance strategy is a popular alternative to TPH where each type in the type hierarchy is separated into its own table with columns for properties that are declared on that type. In CTP4, you would make repeated calls to the MapHierarchy method to do this where you specified which properties belong to each table/type.

public class Product

{

    public int Id { get; set; }

    public string Name { get; set; }

    public Category Category { get; set; }

}

   

public class DiscontionuedProduct : Product

{

    public DateTime DiscontinuedOn { get; set; }

}

 

public class MyContext : DbContext

{

    public DbSet<Product> Products { get; set; }

 

    protected override void OnModelCreating(ModelBuilder modelBuilder)

    {

        modelBuilder.Entity<Product>()

            .MapHierarchy(p => new {

                    p.Id,

                    p.Name

                }).ToTable("Products");

 

        modelBuilder.Entity<DiscontionuedProduct>()

            .MapHierarchy(d => new {

                    d.Id,

                    d.DiscontinuedOn

                }).ToTable("DiscontionuedProducts");

    }

}

 

In CTP5, you no longer have to pass in which properties belong to each table but instead can simple say which table an entity should map to using the ToTable method:

public class Product

{

    public int Id { get; set; }

  public string Name { get; set; }

    public Category Category { get; set; }

}

 

public class DiscontionuedProduct : Product

{

    public DateTime DiscontinuedOn { get; set; }

}

 

public class MyContext : DbContext

{

    public DbSet<Product> Products { get; set; }

 

protected override void OnModelCreating(ModelBuilder modelBuilder)

{

    modelBuilder.Entity<Product>()

        .ToTable("Products");

 

    modelBuilder.Entity<DiscontionuedProduct>()

        .ToTable("DiscontionuedProducts");

}

 

Table-per concrete-type (TPC) inheritance

The Table-per concrete -type(TPC) inheritance strategy is a variation of TPT where each type in the type hierarchy is separated into its own table, but each table contains all of the properties for that type even if the properties came from a base type. In CTP4, you would make repeated calls to the MapHierarchy method to do this where you specified which properties belong to each table/type, making sure that the properties were the complete set needed by that type.

public class Product

{

    public int Id { get; set; }

    public string Name { get; set; }

    public Category Category { get; set; }

}

   

public class DiscontionuedProduct : Product

{

    public DateTime DiscontinuedOn { get; set; }

}

 

public class MyContext : DbContext

{

    public DbSet<Product> Products { get; set; }

 

    protected override void OnModelCreating(ModelBuilder modelBuilder)

    {

        modelBuilder.Entity<Product>()

            .MapHierarchy(p => new {

                    p.Id,

                    p.Name

                }).ToTable("Products");

 

        modelBuilder.Entity<DiscontionuedProduct>()

            .MapHierarchy(d => new {

                    d.Id,

                    d.Name,

                    d.DiscontinuedOn

                }).ToTable("DiscontionuedProducts");

    }

}

 

In CTP5, just like with TPT, you no longer have to pass in which properties belong to each table. There is a helper method on the mapping configuration called MapInheritedProperties which tells Code First that you want all of the inherited properties to be mapped as part of this table as well.

public class Product

{

    public int Id { get; set; }

    public string Name { get; set; }

}

 

public class DiscontionuedProduct : Product

{

    public DateTime DiscontinuedOn { get; set; }

}

 

public class MyContext : DbContext

{

    public DbSet<Product> Products { get; set; }

 

protected override void OnModelCreating(ModelBuilder modelBuilder)

{

    modelBuilder.Entity<Product>()

        .ToTable("Products");

 

    modelBuilder.Entity<DiscontionuedProduct>()

        .Map(mc => {

            mc.MapInheritedProperties();

        }).ToTable("DiscontionuedProducts");

}

 

Entity Splitting

Entity splitting is when a single entity is mapped to columns from multiple tables. In CTP5, you could use multiple calls to MapSingleType to accomplish this. In the example below the Product class is mapped to two tables: Products, which contains Id and Name columns, and SpecialProductInfo which contains Id and the SpecialName columns.

public class Product

{

    public int Id { get; set; }

    public string Name { get; set; }

    public string SpecialName { get; set; }

    public Category Category { get; set; }

}

 

public class MyContext : DbContext

{

    public DbSet<Product> Products { get; set; }

 

    protected override void OnModelCreating(ModelBuilder modelBuilder)

    {

        modelBuilder.Entity<Product>()

            .MapSingleType(p => new {

                    p.Id,

                    p.Name

                }).ToTable("Products");

        modelBuilder.Entity<Product>()

            .MapSingleType(p => new {

                p.Id,

                p.SpecialName

            }).ToTable("SpecialProductInfo");

    }

}

 

In CTP5, you can make use of the mapping configuration class that you access through the Map method:

public class Product

{

    public int Id { get; set; }

    public string Name { get; set; }

    public string SpecialName { get; set; }

    public Category Category { get; set; }

}

 

public class MyContext : DbContext

{

    public DbSet<Product> Products { get; set; }

 

protected override void OnModelCreating(ModelBuilder modelBuilder)

{

    modelBuilder.Entity<Product>()

            .Map(mc => {

                mc.Properties(p => new {

                    p.Id,

                    p.Name

                });

                mc.ToTable("Products");

            })

            .Map(mc => {

                mc.Properties(p => new {

                    p.Id,

                    p.SpecialName

                });

      mc.ToTable("SpecialProductInfo");

            });

}

 

Renaming a Many-to-Many Join Table

Many to many relationships require a join table and Code First in both CTP4 and CTP5 allow you to change the name of the table and the columns of this table. In the example below for CTP4, the Products entity and the Store entity have a many to many relationship. This relationship is mapped using the relationship API methods HasMany and WithMany. Following these methods is a Map method which allows you to specify the join table name and the column names.

public class Product

{

    public int Id { get; set; }

    public string Name { get; set; }

    public ICollection<Store> SoldAt { get; set; }

}

 

public class Store

{

    public int Id { get; set; }

    public string StoreName { get; set; }

    public ICollection<Product> Products { get; set; }

}

 

public class MyContext : DbContext

{

    public DbSet<Product> Products { get; set; }

 

    protected override void OnModelCreating(ModelBuilder modelBuilder)

    {

        modelBuilder.Entity<Product>()

            .HasMany(p => p.SoldAt)

            .WithMany(s => s.Products)

            .Map("ProductsAtStores",

                (p, s) => new {

                    ProductId = p.Id,

                    StoreId = s.Id

                });

    }

}

 

In CTP5, the Map method has a slight change in that you interact with a mapping configuration for the many to many relationship that gives you options about what you can specify:

public class Product

{

    public int Id { get; set; }

    public string Name { get; set; }

    public ICollection<Store> SoldAt { get; set; }

}

 

public class Store

{

    public int Id { get; set; }

    public string StoreName { get; set; }

    public ICollection<Product> Products { get; set; }

}

 

public class MyContext : DbContext

{

    public DbSet<Product> Products { get; set; }

 

protected override void OnModelCreating(ModelBuilder modelBuilder)

{

 

    modelBuilder.Entity<Product>()

        .HasMany(p => p.SoldAt)

        .WithMany(s => s.Products)

        .Map(mc => {

            mc.ToTable("ProductsAtStores");

            mc.MapLeftKey(p => p.Id, "ProductId");

            mc.MapRightKey(s => s.Id, "StoreId");

        });

}

 

Renaming a Foreign Key

There are two kinds of relationships in Entity Framework applications: those where the foreign key property is part of the entity/.NET class, and those where the foreign key is not part of the entity/.NET class. The later, where there is no foreign key property on your entity/.NET class is called an “Independent Association”, and in this case Code First will generate a foreign key column for you and place it on the appropriate table to make the relationship work.

If your entity/.NET class has a foreign key property and you want to rename it, you can do this just like you would rename any other scalar property. If your entity/.NET class is going to use an independent association, you have the option to provide a name for the foreign key column that Code First will use.

In CTP4, this was done by using a dotted syntax to access the primary key property.

public class Category

{

    public int Id { get; set; }

    public string Name { get; set; }

    public ICollection<Product> Products { get; set; }

}

 

public class Product

{

    public int Id { get; set; }

    public string Name { get; set; }

    public Category Category { get; set; }

}

 

public class MyContext : DbContext

{

    public DbSet<Product> Products { get; set; }

    public DbSet<Category> Categories { get; set; }

 

    protected override void OnModelCreating(ModelBuilder modelBuilder)

    {

        modelBuilder.Entity<Product>()

            .MapSingleType(

               p => new {

                   p.Id,

                   p.Name,

                   CategoryId = p.Category.Id

            }

            );

    }

}

 

In CTP5, you can map the name of the generated foreign key column as part of the relationship API:

public class Category

{

    public int Id { get; set; }

    public string Name { get; set; }

    public ICollection<Product> Products { get; set; }

}

 

public class Product

{

    public int Id { get; set; }

    public string Name { get; set; }

    public Category Category { get; set; }

}

 

public class MyContext : DbContext

{

    public DbSet<Product> Products { get; set; }

    public DbSet<Category> Categories { get; set; }

 

    protected override void OnModelCreating(ModelBuilder modelBuilder)

    {

        modelBuilder.Entity<Product>()

            .HasRequired(p => p.Category)

            .WithMany(c => c.Products)

          .IsIndependent()

            .Map(mc => mc.MapKey(c => c.Id, "CategoryId"));

    }

}

Excluding a Property from the Model

In CTP4, you could exclude a property (such as the Product.InternalCode property below) from the model by using the MapSingleType method and excluding it from the list of specified properties.

public class Product

{

    public int Id { get; set; }

    public string Name { get; set; }

    public string InternalCode { get; set; }

}

 

public class MyContext : DbContext

{

    public DbSet<Product> Products { get; set; }

 

    protected override void OnModelCreating(ModelBuilder modelBuilder)

    {

        modelBuilder.Entity<Product>()

            .MapSingleType(

               p => new {

                   p.Id,

                   p.Name

               }

            );

    }

}

 

In CTP5, there are two ways to ignore both types and properties. The first is to use the NotMappedAttribute data annotation:

public class Product

{

    public int Id { get; set; }

    public string Name { get; set; }

    [NotMapped]

    public string InternalCode { get; set; }

}

 

You can also use the fluent API in CTP5 to ignore properties using the ‘Ignore’ method:

public class Product

{

    public int Id { get; set; }

    public string Name { get; set; }

    public string InternalCode { get; set; }

}

 

public class MyContext : DbContext

{

    public DbSet<Product> Products { get; set; }

 

    protected override void OnModelCreating(ModelBuilder modelBuilder)

    {

        modelBuilder.Entity<Product>()

            .Ignore(p => p.InternalCode);

    }

}

 

We are continuing to tune this API as we progress towards the first RTM of Code First so that using it going forward helps make development as productive as possible. We’d appreciate any feedback or questions you have on the new API or how to accomplish certain mapping tasks.

Jeff Derstadt

Entity Framework Team