Entity Framework CTP4 – Code First – Map your POCO entities to different table

The problem :-
Following is the code I use and I get an error given below:-

Contact:-
public class Contact
{
public int ContactID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Title { get; set; }
public DateTime AddDate { get; set; }
public DateTime ModifiedDate { get; set; }
}


Context:-
public class AddressBook : DbContext
{
public DbSet<Contact> Contact { get; set; }
}

The main program:-
using (var context = new AddressBook())
  {
   var contact = new Contact
   {
   ContactID = 10000,
   FirstName = "Brian",
   LastName = "Lara",
   ModifiedDate = DateTime.Now,
   AddDate = DateTime.Now,
   Title = "Mr."
   };
   context.Contact.Add(contact);
   int result = context.SaveChanges();
   Console.WriteLine("Result :- " + result.ToString());
  }

And I get the following error on "context.Contact.Add(contact);":-

System.InvalidOperationException: The model backing the ‘AddressBook’ context has changed since the database was created. Either manually delete/update the database, or call Database.SetInitializer with an IDatabaseInitializer instance. For example, the RecreateDatabaseIfModelChanges strategy will automatically delete and recreate the database, and optionally seed it with new data. at System.Data.Entity.Infrastructure.CreateDatabaseOnlyIfNotExists1.InitializeDatabase(TContext context) at System.Data.Entity.Infrastructure.Database.Initialize() at System.Data.Entity.Internal.InternalContext.Initialize() at System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType) at System.Data.Entity.Internal.Linq.EfInternalQuery1.Initialize() at System.Data.Entity.DbSet1.ActOnSet(Action action,EntityState newState, TEntity entity) at System.Data.Entity.DbSet1.Add(TEntity entity) at CodeFirst.Client.Program.Main(String[] args) in E:\Ashish\Research\VS Solutions\EntityFramework\CodeFirstApproach_EF_CTP4\CodeFirst.Client\Program.cs:line 35

 

Solution :-

The error message says:-
“System.InvalidOperationException: The model backing the ‘AddressBook’ context has changed since the database was created. Either manually delete/update the database, or call Database.SetInitializer with an IDatabaseInitializer instance. For example, the RecreateDatabaseIfModelChanges strategy will automatically delete and recreate the database, and optionally seed it with new data”

so, first look at the Database.SetInitializer() method which you can call in three different ways:-

// 1) This is the default strategy.  It creates the DB only if it doesn't exist
Database.SetInitializer(new CreateDatabaseOnlyIfNotExists<ProductContext>());

// 2) Recreates the DB if the model changes but doesn't insert seed data.
Database.SetInitializer(new RecreateDatabaseIfModelChanges<ProductContext>());

// 3) Strategy for always recreating the DB every time the app is run.
Database.SetInitializer(new AlwaysRecreateDatabase<ProductContext>());

None of the above cases is applicable to us If we have an already existing database with data. However, we still need to call Database.SetInitializer(null) to nullify the default strategy , the first one in the above list – “creates the DB only if it doesn’t exist”. There is a link pointed by Pault which has the following comment by Jeff of EF team:-

Friday, August 06, 2010 11:28 AM by Jeff
@Mark
For those who are seeing this exception:
"The model backing the ‘Production’ context has changed since the database was created. Either manually delete/update the database, or call Database.SetInitializer with an IDatabaseInitializer instance."
Here is what is going on and what to do about it:

When a model is first created, we run a DatabaseInitializer to do things like create the database if it’s not there or add seed data. The default DatabaseInitializer tries to compare the database schema needed to use the model with a hash of the schema stored in an EdmMetadata table that is created with a database (when Code First is the one creating the database). Existing databases won’t have the EdmMetadata table and so won’t have the hash…and the implementation today will throw if that table is missing. We’ll work on changing this behavior before we ship the fial version since it is the default. Until then, existing databases do not generally need any database initializer so it can be turned off for your context type by calling:

Database.SetInitializer<Production>(null);

Jeff

So, I took a leaf out of the above and added that in my main program:-

Database.SetInitializer<AddressBook>(null);
using (var context = new AddressBook())
          {
                   var contact = new Contact
                   {
                       ContactID = 10000,
                       FirstName = "Brian",
                       LastName = "Lara",
                       ModifiedDate = DateTime.Now,
                       AddDate = DateTime.Now,
                       Title = "Mr."
                   };
                   context.Contacts.Add(contact);
                   int result = context.SaveChanges();
                   Console.WriteLine("Result :- " + result.ToString());

               }

After making the above code change, I ran the program and hit a wall again (small part of the error message shown below):-

System.Data.EntityCommandExecutionException: An error occurred while executing the command definition. See the inner exception for details. —> System.Data.SqlClient.SqlException: Invalid object name ‘dbo.Contacts’.   at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
 

Code-first, by convention, assumes that your table in the database is plularized form of your POCO class. For example, If your POCO class is Contact, code-first assumes that the table name is “Contacts” and tries to find the table named “Contacts” to persist the Contact objects to “Contacts”  table. Thats why we see an error above as we dont have a table named “Contacts” in the database. We have table name as “Contact” instead.

So, while working with existing database, What If you want your POCO class point to a differnt table? For example, your POCO class is Contact however your table in the database is also Contact. The table name could be anything. To fix this, you need to map your Contact entity to the correct table name when the model is being created. In the context class, override the OnModelCreating() event handler in the Context class and map the object to the correct table name.
public class AddressBook : DbContext
    {
        public DbSet<Contact> Contacts { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Contact>().MapSingleType().ToTable("tblContact");
        }
    }

If we run the sample now, It would run without any error. I also discussed this question here.

Advertisements
  1. #1 by Matt on January 21, 2011 - 9:36 am

    Hey, Thanks for the post. I’m having an issue calling Database.SetInitializer. My complier is just not recognizing the Database part and is asking if I’m missing a reference.

    I am referencing System.Data.Entity and System.Data.Entity.Infrastructure with no luck. Any suggestions?

  2. #2 by FT on April 7, 2012 - 2:20 am

    thanks for the post, this problem and workaround is not well known and hard to find. so thanks again for sharing. saved me a bundled of time. Not sure when you wrote your post but in version 4.3 of EF, the call would be modelBuilder.Entity().ToTable(“Contact”).

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Random Thoughts

The World as I see it

Simple Programmer

Making The Complex Simple

Ionic Solutions

Random thoughts on software construction, design patterns and optimization.

Long (Way) Off

A tragic's view from the cricket hinterlands

%d bloggers like this: