我已经启用了我的实体框架项目代码优先迁移,并增加了一些迁移该做的事情一样重命名表。 不过,我现在已经删除了数据库,并造成实体框架,基于我的最新数据模型,一个全新的数据库。 如果我尝试运行:
PM> Add-Migration TestMigration
......它告诉我,我需要首先将现有的迁移。 于是我运行:
PM> Update-Database
......但麻烦的是,它试图更新不需要更新数据库; 它已经基于最新的数据模型。 所以,当它试图重新命名,现在不存在的表,我得到一个错误。
有没有一些方法,我可以指示数据迁移,我的数据库运行最新的,并不需要在其上运行任何迁移? 我该怎么办?
我已经找到了一种方法,以表明我的数据库是最新的最新的,和它的(不出所料)的基础上修改__MigrationHistory
表,代码优先迁移用来确定当你运行应用到DB哪些迁移Update-Database
。
顺便说一句,同时研究这个答案我碰到一个很不错的参考来对代码优先迁移命令,可以在这里找到: http://dotnet.dzone.com/articles/ef-migrations-command
当从头开始自动EF创建的数据库,它会永远把一个进入__MigrationHistory
表,该条目将有MigrationId (currentDateTime)_InitialCreate
。 这表示EF刚刚进行过数据库的初始创建。 但是,迁移历史是不会与MigrationId开始,因为你已经开始用别的东西。
以“傻瓜”代码优先迁移,以为你是在最新的迁移,您需要删除(currentDateTime)_InitialCreate
从入门__MigrationHistory
新创建数据库的表,然后将你会一直存在,如果你仍然有旧的数据库,其不得不适用于它的迁移。
所以,先删除一切从新近生成的DB的__MigrationHistory
表。 然后,进入包管理器控制台,然后运行:
PM> Update-Database -Script
从得到的SQL脚本,使出浑身开头的几行:
INSERT INTO [__MigrationHistory]...
然后,运行这些INSERT
新创建的数据库的上下文中的语句。 检查每个这些行现在在你的存在__MigrationHistory
表。 当您下一次运行:
PM> Update-Database
......你应该得到一个消息,说“没有待基于代码的迁移。” 恭喜 - 你已经上当代码优先迁移以为你现在最新的迁移,你可以继续你离开的地方离这里添加新的迁移。
我认为应该有做着这个内置EF代码优先,但...也许还应该加上一些像一些自动化的方式:
PM> Update-Database -MigrationsTableOnly
......由此会揍在迁移表中的现有条目,只需将新的条目到迁移历史记录在您的项目中定义的每个迁移,但实际上没有尝试和执行的迁移。 呃,好吧。
UPDATE
我已经找到一种方法来很好地自动执行此,使用自定义初始化的种子法。 基本上种子方法删除DB时,被创建的现有迁移历史数据,并插入您的迁移历史。 在我的数据库上下文构造,我注册自定义初始化像这样:
public class MyDatabaseContext : DbContext {
public MyDatabaseContext() : base() {
Database.SetInitializer(new MyDatabaseContextMigrationHistoryInitializer());
}
自定义初始化本身看起来是这样的:
/// <summary>
/// This initializer clears the __MigrationHistory table contents created by EF code-first when it first
/// generates the database, and inserts all the migration history entries for migrations that have been
/// created in this project, indicating to EF code-first data migrations that the database is
/// "up-to-date" and that no migrations need to be run when "Update-Database" is run, because we're
/// already at the latest schema by virtue of the fact that the database has just been created from
/// scratch using the latest schema.
///
/// The Seed method needs to be updated each time a new migration is added with "Add-Migration". In
/// the package manager console, run "Update-Database -Script", and in the SQL script which is generated,
/// find the INSERT statement that inserts the row for that new migration into the __MigrationHistory
/// table. Add that INSERT statement as a new "ExecuteSqlCommand" to the end of the Seed method.
/// </summary>
public class MyDatabaseContextMigrationHistoryInitializer : CreateDatabaseIfNotExists<MyDatabaseContext> {
/// <summary>
/// Sets up this context's migration history with the entries for all migrations that have been created in this project.
/// </summary>
/// <param name="context">The context of the database in which the seed code is to be executed.</param>
protected override void Seed(MyDatabaseContext context) {
// Delete existing content from migration history table, and insert our project's migrations
context.Database.ExecuteSqlCommand("DELETE FROM __MigrationHistory");
context.Database.ExecuteSqlCommand("INSERT INTO __MigrationHistory (MigrationId, Model, ProductVersion) VALUES ('201210091606260_InitialCreate', 0x1F8B0800000000000400ECBD07601C499625262F6DCA7B7F4AF54AD7E074A..., '5.0.0.net40')");
context.Database.ExecuteSqlCommand("INSERT INTO __MigrationHistory (MigrationId, Model, ProductVersion) VALUES ('201210102218467_MakeConceptUserIdNullable', 0x1F8B0800000000000400ECBD07601C499625262F6DCA7B7F4..., '5.0.0.net40')");
context.Database.ExecuteSqlCommand("INSERT INTO __MigrationHistory (MigrationId, Model, ProductVersion) VALUES ('201210231418163_ChangeDateTimesToDateTimeOffsets', 0x1F8B0800000000000400ECBD07601C499625262F6D..., '5.0.0.net40')");
context.Database.ExecuteSqlCommand("INSERT INTO __MigrationHistory (MigrationId, Model, ProductVersion) VALUES ('201210251833252_AddConfigSettings', 0x1F8B0800000000000400ECBD07601C499625262F6DCA7B7F4AF54AD7E..., '5.0.0.net40')");
context.Database.ExecuteSqlCommand("INSERT INTO __MigrationHistory (MigrationId, Model, ProductVersion) VALUES ('201210260822485_RenamingOfSomeEntities', 0x1F8B0800000000000400ECBD07601C499625262F6DCA7B7F4AF5..., '5.0.0.net40')");
}
}
转到MigrationHistory
在SQL Server(下表system
文件夹),它具有排了迁移,并在其数据库哈希必须是同一个在迁移文件,只是从数据库到文件复制。
换句话说,你需要同步您的MigrationHistory
实际迁移表。
此实现不需要手工维护的记录被插入到__MigrationHistory
。 迁移是从组件指定的确定。
也许这会有所帮助。
我感谢送给@Jez了最初的想法。
/// <summary>
/// An implementation of IDatabaseInitializer that will:
/// 1. recreate database only if the database does not exist
/// 2. actualize __MigrationHistory to match current model state (i.e. latest migration)
/// </summary>
/// <typeparam name="TContext">The type of the context.</typeparam>
public class CreateDatabaseIfNotExistsAndMigrateToLatest<TContext> : CreateDatabaseIfNotExists<TContext>
where TContext : DbContext
{
private readonly Assembly migrationsAssembly;
/// <summary>
/// Gets the migration metadata for types retrieved from <paramref name="assembly"/>. Types must implement <see cref="IMigrationMetadata"/>.
/// </summary>
/// <param name="assembly">The assembly.</param>
/// <returns></returns>
private static IEnumerable<IMigrationMetadata> GetMigrationMetadata(Assembly assembly)
{
var types = assembly.GetTypes().Where(t => typeof(IMigrationMetadata).IsAssignableFrom(t));
var migrationMetadata = new List<IMigrationMetadata>();
foreach (var type in types)
{
migrationMetadata.Add(
(IMigrationMetadata)Activator.CreateInstance(type));
}
return migrationMetadata.OrderBy(m => m.Id);
}
/// <summary>
/// Gets the provider manifest token.
/// </summary>
/// <param name="db">The db.</param>
/// <returns></returns>
private static string GetProviderManifestToken(TContext db)
{
var connection = db.Database.Connection;
var token = DbProviderServices.GetProviderServices(connection).GetProviderManifestToken(connection);
return token;
}
/// <summary>
/// Gets the migration SQL generator. Currently it is <see cref="SqlServerMigrationSqlGenerator"/>.
/// </summary>
/// <returns></returns>
private static MigrationSqlGenerator GetMigrationSqlGenerator()
{
return new SqlServerMigrationSqlGenerator();
}
/// <summary>
/// Creates the operation for inserting into migration history. Operation is created for one <paramref name="migrationMetadatum"/>.
/// </summary>
/// <param name="migrationMetadatum">The migration metadatum.</param>
/// <returns></returns>
private static InsertHistoryOperation CreateInsertHistoryOperation(IMigrationMetadata migrationMetadatum)
{
var model = Convert.FromBase64String(migrationMetadatum.Target);
var op = new InsertHistoryOperation(
"__MigrationHistory",
migrationMetadatum.Id,
model,
null);
return op;
}
/// <summary>
/// Generates the SQL statements for inserting migration into history table.
/// </summary>
/// <param name="generator">The generator.</param>
/// <param name="op">The operation.</param>
/// <param name="token">The token.</param>
/// <returns></returns>
private static IEnumerable<MigrationStatement> GenerateInsertHistoryStatements(
MigrationSqlGenerator generator,
InsertHistoryOperation op,
string token)
{
return generator.Generate(new[] { op }, token);
}
/// <summary>
/// Runs the SQL statements on database specified by <paramref name="db"/> (<see cref="DbContext.Database"/>).
/// </summary>
/// <param name="statements">The statements.</param>
/// <param name="db">The db.</param>
private static void RunSqlStatements(IEnumerable<MigrationStatement> statements, TContext db)
{
foreach (var statement in statements)
{
db.Database.ExecuteSqlCommand(statement.Sql);
}
}
/// <summary>
/// Initializes a new instance of the <see cref="CreateDatabaseIfNotExistsAndMigrateToLatest{TContext}"/> class.
/// </summary>
/// <param name="migrationsAssembly">The migrations assembly.</param>
public CreateDatabaseIfNotExistsAndMigrateToLatest(Assembly migrationsAssembly)
{
this.migrationsAssembly = migrationsAssembly;
}
protected override void Seed(TContext context)
{
base.Seed(context);
// Get migration metadata for migrationAssembly
var migrationMetadata = GetMigrationMetadata(migrationsAssembly);
// Crate DbContext
var db = Activator.CreateInstance<TContext>();
// Remove newly created record in __MigrationHistory
db.Database.ExecuteSqlCommand("DELETE FROM __MigrationHistory");
// Get provider manifest token
var token = GetProviderManifestToken(db);
// Get sql generator
var generator = GetMigrationSqlGenerator();
foreach (var migrationMetadatum in migrationMetadata)
{
// Create history operation
var op = CreateInsertHistoryOperation(migrationMetadatum);
// Generate history insert statements
var statements = GenerateInsertHistoryStatements(generator, op, token);
// Run statements (SQL) over database (db)
RunSqlStatements(statements, db);
}
}
}