I wanted to use a many-to-many relationship using Entity Framework Code First (v4.1/4.2).
Using pure code first such as this:
using (var context = new MyContext()) { var employee = new Employee { FirstName = "Homer", LastName = "Simpson" }; var territory = new Territory { TerritoryDescription = "Springfield" }; employee.Territories.Add(territory); context.Employees.Add(employee); context.SaveChanges(); }
results in a nice association table
How do you map existing database tables? Like Northwind's customer to customer demographic table relationship:
This is the code I want to write:
using (var context = new MyContext("name=Northwind")) { var demo = new CustomerDemographic(); demo.CustomerTypeID = "BERLIN"; demo.CustomerDesc = "Berliner"; context.CustomerDemographics.Add(demo); //link it to a customer by either end var alfki = context.Customers.Find("ALFKI"); alfki.CustomerDemographics.Add(demo); context.SaveChanges(); }
We have to override DbContext's OnModelCreating and add some mapping. For CodeFirst, you map both sides of the relationship, so you can either put the mapping on Customer or CustomerDemographic - or even both if the mappings agree. A normal foreign key relationship is mapped with ".HasMany|HasOptional|HasRequired" followed by a ".WithMany|WithOptional|WithRequired".
So, from the CustomerDemographic entity, a many to many is just .HasMany(x=>x.Customers).WithMany(z=>z.CustomerDemographics).
In addition, we don't have standard names for our association table so we add a .Map element to specify the table and the left and right key columns.
Note the primary key of CustomerDemographics isn't the 'tableName'+"Id" convention that Code First will expect. So I have to define the key for that. As we have that end of the configuration, we'll define the mapping there.
Here's the code.
modelBuilder.Entity<CustomerDemographic>() //the key isn't standard so specify it .HasKey(x => x.CustomerTypeID) //define both sides of the relationship - HasMany.WithMany .HasMany(x => x.Customers) .WithMany(z => z.CustomerDemographics) //specify mapping information .Map(map => { //the association table name map.ToTable("CustomerCustomerDemo"); //the left side (fk to CustomerDemographic, the entity we're defining) map.MapLeftKey("CustomerTypeID"); //the right side (fk to Customers, the other side) map.MapRightKey("CustomerID"); } );
If we mapped from the Customer entity, the HasMany and WithMany properties are different, and the mapped left and right keys swap round.
Here's the full DbContext for my mini-Northwind mapping:
class MyContext : DbContext { public MyContext(string connectionName) : base(connectionName) { } public DbSet<Employee> Employees { get; set; } public DbSet<Territory> Territories { get; set; } public DbSet<Customer> Customers { get; set; } public DbSet<CustomerDemographic> CustomerDemographics { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { //Database.SetInitializer(new DropCreateDatabaseIfModelChanges<MyContext>()); Database.SetInitializer<MyContext>(null); modelBuilder.Conventions.Remove<System.Data.Entity.Infrastructure.IncludeMetadataConvention>(); modelBuilder.Entity<CustomerDemographic>() //the key isn't standard so specify it .HasKey(x => x.CustomerTypeID) //define both sides of the relationship - HasMany.WithMany .HasMany(x => x.Customers) .WithMany(z => z.CustomerDemographics) //specify mapping information .Map(map => { //the association table name map.ToTable("CustomerCustomerDemo"); //the left side (fk to CustomerDemographic, the entity we're defining) map.MapLeftKey("CustomerTypeID"); //the right side (fk to Customers, the other side) map.MapRightKey("CustomerID"); } ); } }