3

My development DB is 2008 R2 which is where Code First generates the local database with DropCreateIfModelChanges.

My method of deploying to production is to generate scripts from local DB, including data and run that in production. This creates all of the tables, including the EdmMetadata table and populates it with the hash.

Works: Run script in a different 2008 R2 instance, change connection string for the Entity model to point to this production instance, run application.

Does not work: Run script in a different 2005 instance, change connection string for the Entity model to point to this production instance, run application. I get an error indicating the model has changed.

I think this doesn't work because the DB compatibility version is a part of the hash. thus in production it generates a hash and compares it to the hash stored in the EdmMetadata table. The new hash is different because it is generated against a 2005 DB.

I am guessing I would not have this problem if I was generating a 2005 DB locally and deploying that to a 2005 production instance. However I don't have 2005 installed and would rather not require all the developers to have to install it, when 2008 supports a 2005 compatibility mode already.

How can I force EF to generate a DB in 2005 compatibility mode?

1
  • have you tried creating script from PM console directly ? i.e. "Update-Database –Script" - this is part of the migration features (talking about 4.3). Then do it manually. The whole migration part is made for such things, I'm not sure and have no way of testing that right now but I think you should just follow the recommended route here, use migration (and / or scripts before the Db is generated) and you should have no problems. If you're using the 'SqlConnectionFactory' which is default that should be ok, work. Commented Mar 21, 2012 at 22:49

1 Answer 1

2

If the only problem is the model compatibility check, then you can disable database initializers for your context when running in the production environment where you presumably don't need to initialize the database anyway. You can do this in code like so:

Database.SetInitializer<MyContext>(null);

But it's probably better to do it in the app.config/web.config for your production application like so:

<entityFramework>
  <contexts>
    <context type="MyNamespace.MyContext, MyAssembly" disableDatabaseInitialization="true" />
  </contexts>
</entityFramework>

You will need to update to EF 4.3 for this syntax--see http://blogs.msdn.com/b/adonet/archive/2012/01/12/ef-4-3-configuration-file-settings.aspx. There is also a way to do it in EF 4.1: See http://blog.oneunicorn.com/2011/03/31/configuring-database-initializers-in-a-config-file/.

You could also try just updating to to EF 4.3 which doesn't use the EdmMetadata table anymore--it uses the __MigrationHistory table instead. This checks for model compatibility in a different way. It may still flag a difference if Code First would have generated a different database for 2005 than it did for 2008, which is occasionally the case.

You could install SQL Server 2005 Express on your dev box. It's free and would match your production environment better.

Finally, if none of the above work and you need to force Code First to generate a 2005 model/database, then you can do that, but it means using lower-level building blocks. First, you'll need to create the DbModelBuilder yourself and call Entity for each of the entity types for which you have a DbSet declared on your context:

var modelBuilder = new DbModelBuilder();
modelBuilder.Entity<User>();
modelBuilder.Entity<Blog>();

You can do other fluent configuration here or use data annotations as normal. OnModelCreating will not be called so don't put fluent calls there--move them here instead.

Once you have a configured DbModelBuilder you'll need to build and compile to get a compiled model that can be passed to DbContext. It is at this stage that you can pass in "2005" as the provider manifest token.

var compiledModel = modelBuilder
    .Build(new DbProviderInfo("System.Data.SqlClient", "2005"))
    .Compile();

You should now cache this compiled model in your app domain so that you only build and compile it once. (Normally DbContext does this for you when it builds the model, but if you build the model yourself then you need to also do the caching yourself.)

Finally, you will need to pass the compiled model to a constructor of your context every time you need to use it and have that constructor pass the model on to the base constructor.

public class MyContext : DbContext
{
    public MyContext(DbCompiledModel model)
        : base(model)
    {
    }

    public DbSet<User> Users { get; set; }
    public DbSet<Blog> Blogs { get; set; }
}

There are other constructor overloads for passing a name or connection string as well if you need them.

Sign up to request clarification or add additional context in comments.

1 Comment

+1 Thanks, I will try this next time I deploy. I like the idea of doing it in the config as I can put it in the web.Production.config transform specifically for the production build/deploy configuration.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.