EF 4.3 Beta1—released yesterday—integrates Code First Migrations into the EntityFramework NuGet package. Part of this integration means that databases created with Code First no longer include the EdmMetadata table. The databases are instead setup to work directly with Migrations making it easy to migrate the database in the future even if this need is not anticipated when the database is first created. But what exactly happens behind the scenes? And what is the update experience for databases that already contain EdmMetadata? This post goes into some detail to answer these questions.
As described in an earlier post, EdmMetadata was a simple way of determining if the Code First model had been changed since the database was created. However, it couldn’t be used to determine how the model used to create the database was different from the current model. This didn’t matter too much since there was no way to migrate the database from one version to the next. Now that Code First Migrations is available it is important that the model information stored in the database is can be used by Migrations to determine what has changed in the model so that the database can be migrated.
Creating new databases with EF 4.3
The Database.Create method is called when DbContext is used with the Code First approach to create a database. All of the initializers (except the new MigrateDatabaseToLatestVersion initializer) use this method to create a database when needed. In previous versions of EF this method called the ObjectContext.CreateDatabase method to both create the database and execute all the DDL needed to create tables, etc. The EdmMetadata entity would also be included in the Code First model so that the EdmMetadata table was created.
Starting we EF 4.3 Database.Create instead attempts to use Code First Migrations to create the database and perform DDL. Essentially, Database.Create performs an initial automatic migration for you. As part of this process Migrations will create a table called __MigrationHistory and insert a row into this table containing a compressed version of your Code First model. The database is thus setup to use Migrations in the future if you choose to do so.
(Note that the __MigrationHistory table is made a system table if possible. This means that to see it you may need to expand the “System Tables” in SQL Management Studio or your preferred tool.)
Currently Migrations cannot be used to create a database for you when not using Microsoft SQL Server or SQL Server Compact Edition. In these cases ObjectContext.CreateDatabase is still used and the __MigrationHistory table is then added using ObjectContext.CreateDatabaseScript. The result is essentially the same as if an automatic migration had been used.
Checking model compatibility
The CreateDatabaseIfNotExists and DropCreateDatabaseIfModelChanges initializers use the Database.CompatibleWithModel method to determine whether or not the Code First model matches the model that was used to create the database. This method would previously use the model hash stored in the EdmMetadata table. When the __MigrationHistory table is present CompatibleWithModel instead uses the Migrations model differ to determine compatibility. For the most part the results should be the same, although there is now less chance that CompatibleWithModel will produce false negatives where the hashes are different but the underlying model is fundamentally the same.
Upgrading existing EdmMetadata databases
So what happens if you have an existing database that already contains the EdmMetadata table? Well, as long as the database doesn’t get re-created then nothing happens. EF 4.3 still knows how to use the EdmMetadata table to check for model compatibility and will use this method if __MigrationHistory is not present. If the database is not found to be compatible with the current model, then the behavior is the same as it was with previous versions. That is, an exception may be thrown or the database may be dropped and re-created depending on which initializer is being used.
If the database is dropped and re-created, then it will no longer contain the EdmMetadata table because, as described above, Database.Create will instead use Migrations and create the __MigrationHistory table.
Can I keep using EdmMetadata?
EF now no longer ever includes the EdmMetadata entity in Code First models and it is not recommended that it be used. For this reason the EdmMetadata class and the IncludeMetadataConvention have been obsoleted in EF 4.3. That being said, they are still functional, and they could still be used with some work. However, if you do find the need to use them it would be great if you could let the EF team know since any scenario where EdmMetadata is still needed is something we would like to investigate.
Thanks,
Arthur
Hi Arthur,
I am using EF 4.1, and I exclusively use the “model designer” to make changes to my database and entities. For example, if my Users table needs a new column “OwnsAFerrari” of type boolean, I will simply open up the model designer, and add that column, and right-click the designer and select “Generate database from model”. To upgrade my existing live database, I run the create scripts from version 1.0 on a local SQL server, run the create scripts for 2.0 (featuring the new Users column), run a schema compare from VS between the two, and export the output to a SQL editor. I then run this script against my live database. This is my entire database development process. Will I be affected by the EdmMetadata not being supported anymore?
Thank you very much.
Matt McLoughlin
Matt: It should not affect you. EdmMetadata is only used for Code First flows. Any flow that uses an EDMX, including the Model First flow that you desribe, should not be affected.
Thanks,
Arthur
Hi Arthur,
And in the Code First flow (which is my case) how do I migrate an EF4.1” EdmMetadata” based Production DB to a “_MigrationHistory” based one? I couldn’t find any clue about that.
Can I:
1. Create a new DB using EF5 Code first on my original data classes.
2. Make _MigrationHistory table non system
3. Generate creation scripts (structure & data)
4. Play the scripts against my production BDs
5. drop EdmMetadata table
This implies that EF5 will create the same DB structure as EF4.1 (except Edm and history stuff) and also that _MigrationHistory doesn’t contain some “context” dependent information.
Or is there a trivial answer.
I would like to migrate to EF5 but don’t dare to.
Thanks in advance.
Bernard
Bernard,
This post contains some good info: http://www.ladislavmrnka.com/2012/03/ef-4-3-migrations-and-existing-database/
Thanks,
Arthur
Hi Arthur,
I found that because new “__MigrationHistory” table is System now, script for it is not included into Web Deployment package, which is generated by Publish VS feature
Because of that there is exception after publishing my web application, which states that database was not properly created.
Are you or Web Deployment team going to fix it in some way and allow “__MigrationHistory” to be picked up by Web Deploy?
Thanks,
Andrey
@Andrey I’ve been following up with various people on this issue. The Web Deploy team are currently working on adding support for Migrations so that this will just work. For now you will need to either do something to make the table not a system table. (Migrations doesn’t care whether the table is marked as a system table or not.)
One way to make sure that __MigrationHistory is not a system table is to wrap the SQL generator. This only works if you do it before the database is created and then use Migrations to explicitly create the database with a call to update-database. Create a class that derives from SqlServerMigrationSqlGenerator and override GenerateMakeSystemTable so that it does nothing:
public class NonSystemTableSqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
protected override void GenerateMakeSystemTable(System.Data.Entity.Migrations.Model.CreateTableOperation createTableOperation)
{
}
}
Now set an instance of this new class in your Migrations Configuration:
public Configuration()
{
AutomaticMigrationsEnabled = false;
SetSqlGenerator("System.Data.SqlClient", new NonSystemTableSqlServerMigrationSqlGenerator());
}
If you have an existing __MigrationHistory table and want to make it non-system then you’ll have to do some work in SQL. This worked for me, but my SQL isn’t great so it may not be the best it can be:
SELECT *
INTO [TempMigrationHistory]
FROM [__MigrationHistory]
DROP TABLE [__MigrationHistory]
EXEC sp_rename 'TempMigrationHistory', '__MigrationHistory'
Hope this helps.
Thanks,
Arthur
@Arthur, thanks for your reply. If Web Deploy team is going to take care about Migrations, its fine for me. If I can use these workarounds, it’s also fine for me :)
Is there a way to disable this functionality? Im using plain CLR objects and EF5-beta1 and .NET 4. Every time the app starts up these queries are executed. The solution for EF4x does not work; which was to remove the IncludeMetadataConvention convention.
SELECT
[GroupBy1].[A1] AS [C1]
FROM ( SELECT
COUNT(1) AS [A1]
FROM [dbo].[__MigrationHistory] AS [Extent1]
) AS [GroupBy1]
SELECT TOP (1)
[Extent1].[Id] AS [Id],
[Extent1].[ModelHash] AS [ModelHash]
FROM [dbo].[EdmMetadata] AS [Extent1]
ORDER BY [Extent1].[Id] DESC
@Craig If you don’t want any database initialization to happen, then disable the database initialer for your context with Database.SetInitializer(null) or with an appropriate entry in the config file.
Thanks! It took one more little tweak. It’s a generic for the DbContext class which I put in the static constructor of my extended DbContext class.
public class TagsDataContext : DbContext {
static TagsDataContext()
{
Database.SetInitializer(null);
}
The company I am working for updaded to entity framework 4.3.
Now a warnning is being produced stating:
‘System.Data.Entity.Infrastructure.IncludeMetadataConvention’ is obsolete when
This occurs in the overriding of the OnModelCreating method when the following line is called:
modelBuilder.Conventions.Remove();
I beleive the Remove()removes the EdmMetadata table.
My question is can I just remove the line of code modelBuilder.Conventions.Remove
and will the code work identically the same before the upgrade to entity framework 4.3 was perfromed????????????
The EdmModelDiffer class is an internal class and I can get a reference to it so I can not replace IncludeMetadatConvention with EdmModelDiffer in the code.
I just was handed this project and my knowledge of the inner working of Entity Framework is nill.
It appears that EdmMetadata has been replaced with an internal: called
I have searched alot of website on this issue and most of the websites go off on theory and tangents and ef framework this and that, when I need to know is how to replace the line of code:
modelBuilder.Conventions.Remove();
so the code still works the same as before..
@steve fred IncludeMetadataConvention is no longer used. In other words removing it is now a no-op because it was never added in the first place. So, yes, you should just delete that line of code. However, I’m curious as to why you are updating to 4.3 when EF5 has been out for quite some time. Why not update to EF5?