# Wednesday, March 21, 2012
EF Code First has a neat method to merge in the values of a DTO into an mapped entity.

context.Entry(entity).CurrentValues.SetValues(dataTransferObject);

The DTO will generally have a primary key property, and you can use that to determine if it is a new record or a modification. Here's a method that does that:
public static T Merge<T>(this DbContext context, object dataTransferObject)
             where T : class
        {
            if (context == null) throw new ArgumentNullException("context");
            if (dataTransferObject == null) throw new ArgumentNullException("dataTransferObject");
 
            var property = FindPrimaryKeyProperty<T>(context);
            //find the id property of the dto
            var idProperty = dataTransferObject.GetType().GetProperty(property.Name);
            if (idProperty == null)
                throw new InvalidOperationException("Cannot find an id on the dataTransferObject");
            var id = idProperty.GetValue(dataTransferObject, null);
            //has the id been set (existing item) or not (transient)?
            var propertyType = property.PropertyType;
            var transientValue = propertyType.IsValueType ?
                Activator.CreateInstance(propertyType) : null;
            var isTransient = Equals(id, transientValue);
            T entity;
            if (isTransient)
            {
                //it's transient, just create a dummy
                entity = CreateEntity<T>(id, property);
                //if DatabaseGeneratedOption(DatabaseGeneratedOption.None) and no id, this errors
                context.Set<T>().Attach(entity);
            }
            else
            {
                //try to load from identity map or database
                entity = context.Set<T>().Find(id);
                if (entity == null)
                {
                    //could not find entity, assume assigned primary key
                    entity = CreateEntity<T>(id, property);
                    context.Set<T>().Add(entity);
                }
            }
            //copy the values from DTO onto the entry
            context.Entry(entity).CurrentValues.SetValues(dataTransferObject);
            return entity;
        }
 
 
        private static PropertyInfo FindPrimaryKeyProperty<T>(IObjectContextAdapter context)
            where T : class
        {
            //find the primary key
            var objectContext = context.ObjectContext;
            //this will error if it's not a mapped entity
            var objectSet = objectContext.CreateObjectSet<T>();
            var elementType = objectSet.EntitySet.ElementType;
            var pk = elementType.KeyMembers.First();
            //look it up on the entity
            var propertyInfo = typeof(T).GetProperty(pk.Name);
            return propertyInfo;
        }
 
        private static T CreateEntity<T>(object id, PropertyInfo property)
            where T : class
        {
            // consider IoC here
            var entity = (T)Activator.CreateInstance(typeof(T));
            //set the value of the primary key (may error if wrong type)
            property.SetValue(entity, id, null);
            return entity;
        }

posted on Wednesday, March 21, 2012 7:18:25 AM (Romance Standard Time, UTC+01:00)  #    Comments [0]
# Tuesday, March 20, 2012
When the Code First project get a detached entity from the UI, it may need to check if it is a new (transient) entity - which can be added to the DbSet - or an existing entity that has been modified. You can then implement an AddOrUpdate method.

To do that, it needs to know what the primary key of the entity is, and read the value.

The easiest way to do is generically is for all entities to have a standard interface or abstract base.
var id = ((IEntity) entity).Id;
if (id == default(int))
{
    //add
}
else
{
    //update
}
If you use the [Key] attribute you can also use that to discover the primary key of the entity, whatever the type.

Finally,  you can use EF's internal metadata.
public static bool IsTransient<T>(DbContext context, T entity)
    where T : class
{
    //find the primary key
    var objectContext = ((IObjectContextAdapter)context).ObjectContext;
    //this will error if it's not a mapped entity
    var objectSet = objectContext.CreateObjectSet<T>();
    var elementType = objectSet.EntitySet.ElementType;
    var pk = elementType.KeyMembers.First();
    //look it up on the entity
    var propertyInfo = typeof(T).GetProperty(pk.Name);
    var propertyType = propertyInfo.PropertyType;
    //what's the default value for the type?
    var transientValue = propertyType.IsValueType ? Activator.CreateInstance(propertyType) : null;
    //is the pk the same as the default value (int == 0, string == null ...)
    return propertyInfo.GetValue(entity, null) == transientValue;
}

posted on Tuesday, March 20, 2012 5:29:09 AM (Romance Standard Time, UTC+01:00)  #    Comments [0]
# Monday, March 19, 2012
Last time I was adding a new record with a reference to a dummy record. I marked the reference as Unchanged so Code First wouldn't try to validate or save it.

var johnCarter = new Movie() { Title = "John Carter" };
johnCarter.DirectorId = andrewStantonId;
context.Movies.Add(johnCarter);
//after it's added, change the status of the reference
context.Entry(johnCarter.Director).State =
EntityState.Unchanged;
context.SaveChanges();

Can you set all the references on any entity?

var johnCarter = new Movie() { Title = "John Carter" };
johnCarter.DirectorId = andrewStantonId;
context.Movies.Add(johnCarter);
//after it's added, change the status of the reference
MarkNavigationPropertiesUnchanged(johnCarter);
context.SaveChanges();

We have to look into the underlying EF model.

private static void MarkNavigationPropertiesUnchanged<T>(DbContext context, T entity)
    where T : class
{
    var objectContext = ((IObjectContextAdapter)context).ObjectContext;
    var objectSet = objectContext.CreateObjectSet<T>();
    var elementType = objectSet.EntitySet.ElementType;
    var navigationProperties = elementType.NavigationProperties;
    //the references
    var references = from navigationProperty in navigationProperties
                        let end = navigationProperty.ToEndMember
                        where end.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne ||
                        end.RelationshipMultiplicity == RelationshipMultiplicity.One
                        select navigationProperty.Name;
    //NB: We don't check Collections. EF wants to handle the object graph.
 
    var parentEntityState = context.Entry(entity).State;
    foreach (var navigationProperty in references)
    {
        //if it's modified but not loaded, don't need to touch it
        if (parentEntityState == EntityState.Modified &&
            !context.Entry(entity).Reference(navigationProperty).IsLoaded)
            continue;
        var propertyInfo = typeof(T).GetProperty(navigationProperty);
        var value = propertyInfo.GetValue(entity, null);
        context.Entry(value).State = EntityState.Unchanged;
    }
}

This code only fixes the references to single entities (like movie.Director) - not collections (like director.Movies). It's possible to discover and iterate the collections to change their status, but you'll likely get exceptions from EF because its model is broken.

posted on Monday, March 19, 2012 6:08:43 PM (Romance Standard Time, UTC+01:00)  #    Comments [0]
# Sunday, March 18, 2012
You’re adding a new record, which has a reference to an existing object.

var johnCarter = new Movie() { Title = "John Carter" };
johnCarter.Director = new
Director { Id = andrewStantonId };
context.Movies.Add(johnCarter);
context.SaveChanges();

SaveChanges() will save the new Movie- but it also saves a new Director record (which gets a new Id, even though you set it manually). When you add an entity to a DbSet, the entire object graph is marked as “Added”.

You could do a Find to load the Director record from the database, but it is a pointless database access that you don’t need. It just needs to save the Movie record with a known directorId.

In NHibernate you can use session.Load<Director>(andrewStantonId) which will create an empty proxy object without hitting the database. Only if you use one of the proxy properties (like director.Name) will it hit the database to load the record. EF Code First doesn’t have this feature.

One way round it to add a foreign key Id property to the Movie record:

       public virtual Director Director { get; set; }
       public int? DirectorId { get; set; }

You can then set the DirectorId directly. The two properties are not kept in step automatically, so setting the DirectorId doesn’t cause Director to load from the database. Foreign key Id properties are convenient, but your object model is “denormalized”.

The alternative is to mark the dummy record as unchanged. There are two ways.

One is to set the context.Entry state for the dummy reference AFTER the new record has been added.

var johnCarter = new Movie() { Title = "John Carter" };
johnCarter.DirectorId = andrewStantonId;
context.Movies.Add(johnCarter);
//after it's added, change the status of the reference
context.Entry(johnCarter.Director).State =
EntityState.Unchanged;
context.SaveChanges();

The second way is to create the dummy reference by Attaching it.

//attach the dummy director record
var andrewStanton = new
Director { Id = andrewStantonId};
context.Directors.Attach(andrewStanton);
//now we have an "unchanged" director record to attach
var johnCarter = new
Movie() { Title = "John Carter" };
johnCarter.Director = andrewStanton;
context.Movies.Add(johnCarter);
context.SaveChanges();

posted on Sunday, March 18, 2012 8:04:08 AM (Romance Standard Time, UTC+01:00)  #    Comments [0]
# Saturday, March 17, 2012
When you are a detached entity returns from the UI, you normally save the update like this:
public void UpdateMovie(Movie movie)
{
    using (var context = new DomainContext())
    {
        //attach it
        context.Movies.Attach(movie);
        //mark it as modified
        context.Entry(movie).State = EntityState.Modified;
        //save - but saves all properties...
        context.SaveChanges();
    }
}
The update property saves all the properties - but what if we only wanted to save certain properties? Like this:
update [dbo].[Movies]
set [BoxOffice] = @0
where ([Id] = @1)

The safest way is to do it manually (and you avoid mass assignment vulnerabilities). You have to reload the entity from the database.
public void UpdateMovieBoxOffice(Movie movie)
{
    using (var context = new DomainContext())
    {
        //get database version
        var databaseMovie = context.Movies.Find(movie.Id);
        //manually copy the values
        databaseMovie.BoxOffice = movie.BoxOffice;
        //save
        context.SaveChanges();
    }
}
The UI may be able to track changes (IPropertyNotifyChanged or similar) and give the data service a list of the changed properties. If so, we can use the context.Entry to specify the modified properties. The SQL UPDATE statement will update only those properties.
public void UpdateMovieProperties(Movie movie, IList<string> propertyNames)
{
    using (var context = new DomainContext())
    {
        //attach it
        context.Movies.Attach(movie);
        //use the context entry
        DbEntityEntry<Movie> entry = context.Entry(movie);
        foreach (var propertyName in propertyNames)
        {
            //modify the specific property states only
            entry.Property(propertyName).IsModified = true;
        }
        //save
        context.SaveChanges();
    }
}

The other way is to detect the changes by comparing them to the database. This is similar to the second method, but we use entry.GetDatabaseValues() to get the database values and then compare them. As only the changed properties are marked as modified, the UPDATE statement uses only those properties.
public void UpdateMovieChangedProperties(Movie movie)
{
    using (var context = new DomainContext())
    {
        //attach it
        context.Movies.Attach(movie);
        //use the context entry
        DbEntityEntry<Movie> entry = context.Entry(movie);
        //do a database call to get the state
        var databaseValues = entry.GetDatabaseValues();
        foreach (var propertyName in databaseValues.PropertyNames)
        {
            //modify the specific property states only
            entry.Property(propertyName).IsModified = true;
        }
        //save
        context.SaveChanges();
    }
}
We don't take account of Complex Properties here (the DbPropertyEntries can be nested).


posted on Saturday, March 17, 2012 11:56:43 AM (Romance Standard Time, UTC+01:00)  #    Comments [0]
# Wednesday, February 22, 2012
In EF Code First, context.SaveChanges() automatically does validations.
But lazy loading can collide with validations.

In our model, a Product must have a Category.

public class Product
{
    [Key]
    public int ProductId { getset; }
 
    [Required]
    [StringLength(40)]
    public string ProductName { getset; }
 
    [Required]
    public virtual Category Category { getset; }
}

Let's try this...
using (var context = new NorthwindContext())
{
    //find a specific product
    var product = context.Products.Find(147);
    //set a new name
    product.ProductName = Guid.NewGuid().ToString();
 
    //ERROR!
    context.SaveChanges();
}


Oh no, System.Data.Entity.Validation.DbEntityValidationException. "Validation failed for one or more entities." How can that be? We just loaded it from the database, it must be correct?
The exception says that Category is null. But it exists in the database.

If you inspect it in the debugger, you can see the category ... and if you continue the SaveChanges succeeds. The debugger triggered a lazy load, so it works.

context.SaveChanges() internally turns off lazy loading before validating. Which is good, because you don't want unnecessary database access. But when the reference is required, the validation doesn't recognise this is a proxy which has a categoryId but hasn't loaded it.

Solution 1- no auto validation


One solution is to turn off validation. When you are setting individual properties with values you've already validated, you do not need it here.

context.Configuration.ValidateOnSaveEnabled = false;
context.SaveChanges();

The SQL, by the way, is update [dbo].[Products] set [ProductName] = @0 where ([ProductID] = @1).
(Check it by hooking up a SQL logger, as described here)

An alternative is to cheat the model- ensure references are not Required.

But if you have a product coming back from a UI for insert or update, you need to manually validate that it always has a category.

Solution 2- ensure required references are loaded


This triggers extra database access, but you can keep the automatic validation.

//find a specific product
var product = context.Products.Find(147);
//make sure it's loaded
context.Entry(product).Reference(p => p.Category).Load();

If you load from a query, you can use an .Include(p => p.Category) which will load the product with a join to the category table, so there is only one sql statement. This will bypass the internal cache so if it was loaded the same product earlier, you can't save any data access.
var product = context.Products
    .Include(p => p.Category)
    .First(p => p.ProductId == 147);

Solution 3- Add a foreign key Id property


You can specify foreign key Id properties in addition to the instance property.
//it's not nullable so it's implicitly required
public int CategoryId { getset; }
 
public virtual Category Category { getset; }


Note the CategoryId is now required. The Category instance isn't. Validation can check the CategoryId, and we can set it directly without having to load the instance.

You have to map this arrangement (unless you like to see an EntityCommandCompilationException with the inner exception message being "More than one item in the metadata collection match the identity 'CategoryId'." - I didn't).
In the EntityTypeConfiguration<Product> the mapping must point to the foreign key id property.
HasRequired(x => x.Category)
    .WithMany()
    .HasForeignKey(p => p.CategoryId);


We've "denormalized" our entity model here, but it makes dealing with detached objects and viewmodels a little easier.

But won't the CategoryId and Category properties get out of step? If you set one, which gets persisted?

Let's set the foreign key id property.
//find a specific product
var product = context.Products.Find(146);
 
//it's category 4 in both
Console.WriteLine(product.Category.CategoryId);
Console.WriteLine(product.CategoryId);
 
//set the categoryId property
product.CategoryId = 1;
 
//we've just set it, so it's 1
Console.WriteLine(product.CategoryId);
//oh no, still says 4!
Console.WriteLine(product.Category.CategoryId);


context.SaveChanges() does the right thing- it saves the new value, 1, assigned to the id property.

Let's set the instance.
//find a specific product
var product = context.Products.Find(146);
 
//it's category 1 in both
Console.WriteLine(product.Category.CategoryId);
Console.WriteLine(product.CategoryId);
 
//set the category instance
product.Category = context.Categories.Find(2);
 
//we've just set it, so it's 2
Console.WriteLine(product.Category.CategoryId);
//oh no, still says 1!
Console.WriteLine(product.CategoryId);


But again, context.SaveChanges() does the right thing- it saves the new value, 2, assigned to the instance.

So it looks like persistence always works as you'd expect, but the properties get out of step. You must be careful in your workflow. This won't work...
product.CategoryId = 1;
var returnMessage = "Category updated to " + product.Category.CategoryName;


Conclusion

In most cases I'd prefer to remove validation on save with context.Configuration.ValidateOnSaveEnabled = false - as long as the values were validated downstream (perhaps in the UI validation framework).

But exposing the foreign key property is undoubtably convenient when the UI just sends you categoryId= 1 and you don't want to load that category from the database just so you can persist product.
posted on Wednesday, February 22, 2012 11:45:48 AM (Romance Standard Time, UTC+01:00)  #    Comments [0]
# Friday, February 17, 2012
There is a risk of unbounded result sets when using navigation collections.

If you try to do paging (Skip/Take) on a navigation collection, you actually load all the related entities and then page in memory. Opps.
//unbounded result set - there could be 1000s!
var products = category.Products
    .OrderBy(p => p.ProductName) //you have to order
    .Skip((page - 1) * pageSize)
    .Take(pageSize);
You have the same issue with a simple .Count, as shown in my last post.

The solutions are the same. You can use a filter directly on the products DbSet.
var pagedProductsByCategory = context.Products
    //have to specify primary keys here- can't match on "category"
    .Where(p => p.Category.CategoryId == category.CategoryId)
    .OrderBy(p => p.ProductName) //you have to order
    .Skip((page - 1) * pageSize)
    .Take(pageSize);
Or you can use the context.Entry(x).Collection(y).Query(). This is the equivalent of an NHibernate CreateFilter.
var pagedProducts = context.Entry(category)
    //from the DbEntityEntry, get the navigation property
    .Collection(x => x.Products)
    //turn it into a query
    .Query()
    //page
    .OrderBy(p => p.ProductName) //you have to order
    .Skip((page - 1) * pageSize)
    .Take(pageSize);
To remove temptation, you might want to remove the collection navigation property. In this case, category has no Products collection (the many end of the foreign key), but Product has a Category property (the 0.1 end of the foreign key).

You can specify the mapping in an EntityTypeConfiguration<Product> class map. Because the many end isn't defined, you use an empty .WithMany().
HasOptional(x => x.Category)
    .WithMany() //.WithMany(c => c.Products)
    .Map(m => m.MapKey("CategoryID"));

Remember you can (and should) be profiling your generated SQL, for instance with the EFTracingProvder as shown here.




posted on Friday, February 17, 2012 7:37:58 AM (Romance Standard Time, UTC+01:00)  #    Comments [0]
# Thursday, February 16, 2012
Watch out for unbounded result sets when using navigation properties.

using (var context = new NorthwindContext()))
{
    var category = context.Categories.Find(1); //Beverages
    Console.WriteLine(category.Products.Count);
}

This loads ALL the products for the category, and then counts them.

Fine for a small result set, not so good if you have 1000s of products per category.

Simple solution: use a filter on products.

Console.WriteLine(context.Products
    .Count(p => p.Category.CategoryId == category.CategoryId));

This generates SQL in the form "SELECT COUNT(*) FROM Products WHERE CategoryID = @p"

Alternative solution: use context.Entry with .Query()

Console.WriteLine(context.Entry(category)
    .Collection(x => x.Products)
    .Query()
    .Count());
posted on Thursday, February 16, 2012 1:53:47 PM (Romance Standard Time, UTC+01:00)  #    Comments [0]
# Tuesday, February 14, 2012
I wanted to log the SQL so I can profile a Entity Framework Code First application.

MVC Mini-Profiler only works in an ASP MVC application- not in console or unit tests.

The tracing and caching providers for Entity Framework expect ObjectContexts (EF 4.0), not DbContexts. But we can make them work.

Scenario:
I have a code first project with my DbContext, called NorthwindContext.
I have a unit test project, with a test that uses NorthwindContext

Here's the steps.

1. Download the providers.
2 (Optional): review the Q&A and apply some of the suggested patches.
3. Build the solution.
4. The unit test project will reference the dlls from the tracing provider
EFProviderWrapperToolkit.dll
EFTracingProvider.dll
5. Add an App.config to the unit test project something like this:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <connectionStrings>
    <add name="NorthwindContext" 
         providerName="System.Data.SqlClient" 
         connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=Northwind;Integrated Security=True;Pooling=False;MultipleActiveResultSets=True" />
  </connectionStrings>
  <system.data>
    <DbProviderFactories>
      <add name="EF Tracing Data Provider" 
           invariant="EFTracingProvider" 
           description="Tracing Provider Wrapper" 
           type="EFTracingProvider.EFTracingProviderFactory, EFTracingProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=def642f226e0e59b" />
      <add name="EF Generic Provider Wrapper" 
           invariant="EFProviderWrapper" 
           description="Generic Provider Wrapper" 
           type="EFProviderWrapperToolkit.EFProviderWrapperFactory, EFProviderWrapperToolkit, Version=1.0.0.0, Culture=neutral, PublicKeyToken=def642f226e0e59b" />
    </DbProviderFactories>
  </system.data>
</configuration>
6. In the DbContext, we need to use two of the base constructors
        public NorthwindContext()
        {
            //default ctor, uses app.config connection string named "NorthwindContext"
        }
 
        public NorthwindContext(DbConnection connection)
            :base(connection,true)
        {
           //ctor uses for tracing 
        }
7. In my test, you need to use the overload that takes the DbConnection.
            using (var context = new NorthwindContext(
                CreateConnectionWrapper(@"name=NorthwindContext")))
            {
 
                //profile this!
                var product = context.ProductCollection.Find(1);
            }
8. And add the CreateConnectionWrapper method:
private static DbConnection CreateConnectionWrapper(string nameOrConnectionString)
{
    var providerInvariantName = "System.Data.SqlClient";
    var connectionString = nameOrConnectionString;
    //name=connectionName format
    var index = nameOrConnectionString.IndexOf('=');
    if (index > 0 && nameOrConnectionString.Substring(0, index).Trim()
        .Equals("name"StringComparison.OrdinalIgnoreCase))
    {
        nameOrConnectionString = nameOrConnectionString
            .Substring(index + 1).Trim();
    }
    //look up connection string name
    var connectionStringSetting =
        ConfigurationManager.ConnectionStrings[nameOrConnectionString];
    if (connectionStringSetting != null)
    {
        providerInvariantName = connectionStringSetting.ProviderName;
        connectionString = connectionStringSetting.ConnectionString;
    }
    //create the special connection string with the provider name in it
    var wrappedConnectionString = "wrappedProvider=" + 
        providerInvariantName + ";" + 
        connectionString;
    //create the tracing wrapper
    var connection = new EFTracingConnection
                            {
                                ConnectionString = wrappedConnectionString
                            };
    //hook up logging here
    connection.CommandFinished +=
        (sender, args) => Console.WriteLine(args.ToTraceString());
    return connection;
}
This should cope with connection strings in the 3 common forms ("Northwind", "name=Northwind" and "Data Source=.\SQLEXPRESS;Initial Catalog=Northwind ...")

Note the line to hook up logging (subscribing to the connection.CommandFinished event). We could simply have used
EFTracingProviderConfiguration.LogToConsole = true;

Or you can hook up to log4net or EntLib logging to those tracing events.



posted on Tuesday, February 14, 2012 2:11:00 PM (Romance Standard Time, UTC+01:00)  #    Comments [1]
# Sunday, February 05, 2012

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

image

How do you map existing database tables? Like Northwind's customer to customer demographic table relationship:

image

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");
            }
        );
    }
}
posted on Sunday, February 05, 2012 9:42:13 PM (Romance Standard Time, UTC+01:00)  #    Comments [0]