I would like to implement a custom database initialization strategy so that I can generate the database schema and apply it to an EXISTING EMPTY SQL database using a supplied User ID and Password.
Unfortunately the built-in strategies don’t provide what I’m looking for:
// The default strategy creates the DB only if it doesn't exist - but it does
// exist so this does nothing
Database.SetInitializer(new CreateDatabaseOnlyIfNotExists<DataContext>());
// Drops and re-creates the database but then this breaks my security mapping and
// only works if using a “Trusted" connection
Database.SetInitializer(new RecreateDatabaseIfModelChanges<DataContext>());
// Strategy for always recreating the DB every time the app is run. – no good for
// what I want
Database.SetInitializer(new AlwaysRecreateDatabase<DataContext>());
I have worked out the following but this does not create the ModelHash so I’m unable to use "context.Database.ModelMatchesDatabase()" to validate that the database schema has been created and prevent multiple initializations:
public class Initializer : IDatabaseInitializer<DataContext>
{
Public void InitializeDatabase(DataContext context)
{
// this generates the SQL script from my POCO Classes
var sql = context.ObjectContext.CreateDatabaseScript();
// As expected - when run the second time it bombs out here with "there is already an
// object named xxxxx in the database"
context.ObjectContext.ExecuteStoreCommand(sql);
this.seed(context)
context.SaveChanges();
}
}
Questions:
Does anyone know how I can get/create the model hash? (which is an EdmMetadata Entity)
-Or-
Is there a better way of doing this in general using the Code First CTP?
I took a slightly different approach to this problem. This seems like as good a place as any to share the results.
I want to only create tables that don't already exist in the database. This has the benefit of being able to roll out new tables without erasing the rest of the database.
This also helps if you have multiple data contexts in an inheritance chain. For example, if you split your application into different assemblies. You might have a data context in a "core" module, and then inherit it in a different assembly for add-on modules. This configuration works fine, but the built-in Drop/Create initializers don't like it because the model hash is changing all the time. By checking table existance, initialization takes slightly longer, but then you have none of these issues.
Anyway, here's the code:
I too was looking for a good solution since godaddy does not allow drop/creation of database and thus no tables created. Since the newer version of Entity Framework has obsoleted EDMData, I modified Alex's code to see if a DropMeToRecreateDatabase table exists or not, if it doesnt exist, it deletes all tables and recreates new tables.
I ran into the same problem. I didn't really solve it, but I managed to get a little nasty workaround running, so i can deploy my solution to AppHarbor ;)
Its a IDatabaseInitializer implementation, that doesn't delete the db, but just nukes all the constraints and tables, and then uses the ObjectContext.CreateDatabaseScript() method to generate the sql, and then I execute it as a storecommand. A lot like the above implementation in the question.
But i also added functionality to create a hash from the model and save it in db, and when it runs again it checks if the current model-hash matches the one i db. Just like the real code-first implementation.
I couldn't make it work with the build in context.Database.CompatibleWithModel(true) - but this should work just as well, and seeing as its a temporary workaround it should be fine.
Just to contribute to @Luhmann's solution, here's mine but slightly changed to drop the FK and PK properly.
This is the easiest way to get EF Code First running on AppHarbor!
Using the
EdmMetadata.TryGetModelHash(context)
function to check when the model doesn't match the database and showing an error with the new code that needs to be used after you run alteration scripts.PopulateOnly : Only creates objects when the database is empty
I thought I'd post my own version of the Initializer which I'm currently using on appharbor to populate an existing database. It will also try to do a create if the database doesn't exists and throws if a change is detected (sorry no automatic updating yet). I hope someone finds it useful.
The Entity Designer Database Generation Power Pack will do this. Not sure if it works with Code First yet, but worth a shot.