如何强制实体框架来标记特定的迁移为已应用?(How do I force Entity Framew

2019-08-06 12:25发布

我使用实体框架5中的一个项目,我已经得到了迁移启用。

这里的情景:

一个新的开发(DEV1)开始工作,建立从源头项目。 还有因为以前开发商一直工作在过去的项目中的现有迁移。

当开发者在首次运行时的ASP.NET MVC项目,数据库自动生成,没有出现任何错误。

在此之后,然而,不同的开发商(装置2)增加了一个新的迁移。 当DEV1尝试运行Update-Database ,所有以前的迁移正在试图运行。 但是,他们已经得到了应用,因为他们的初始模型的一部分DEV1看到它。 这通常会导致一个模式错误,因为它试图申请已存在的模式更改。

所以,最佳的,这将是巨大的,只是“快进”的本地数据库当前迁移。 但我不知道的方式做到这一点。 另外,有一些其他的方式来初始化数据库,这样我可以在初始化过程中应用所有的迁移?

Answer 1:

我想出了一个黑客。

运行Update-Database -Script

挑选出已运行的所有迁移

INSERT INTO [__MigrationHistory] ([MigrationId], [Model], [ProductVersion]) VALUES

打开SQL Server Management Studio中,并手动运行这些SQL语句。

新移民应该正常工作。



Answer 2:

在团队环境中的实体框架迁移可能会非常棘手。 特别是当与源控制相结合。 这听起来像你正在运行到不同的代码优先迁移正在针对不同的开发数据库运行的问题。

已针对特定数据库运行的所有迁移存储在__MigrationHistory表。 如果你在你的项目中有一个迁移文件和EF没有看到它在它的去尝试,当你运行更新,数据库执行它的__MigrationHistory表。 如果EF欺骗,以为它已经通过将记录到MigrationHistory这些应用迁移文件将首先打破的EF代码的最好的功能之一。 您将无法作用回用更新的数据库-TargetMigration您的迁移。

如果每个开发都有自己的一套迁移并有可能为他们当你运行更新的数据库有重叠的变化这可能会导致各种SQL错误的。

我对这个解决方案是忽略具有积极的发展做(因为他们往往是tweeked很多)的所有迁移文件。 当一个开发获取新的变更集他们只是创造自己的地方迁移。

当我准备发布一个新的功能,以生产老子滚所有这些变化成一个大的迁移和我一般,我增加我的应用程序的版本号来给它命名。 我检查这些文件迁移到源代码控制。 这些充当我的主人迁移文件带来任何新的数据库最新生产。

当我做这些主迁移一个新的开发者都回作用的本地更改的最后一个主要版本,删除他们不再需要(因为它们都属于主迁移)中的那些,然后运行更新的数据库。



Answer 3:

我花了道格的技术,创建了自动化它DatabaseInitializer。

只要使用

 Database.SetInitializer(new CreateDbWithMigrationHistoryIfNotExists<EntityContext, Configuration>());

在你的DbContext,它会创建一个新的数据库,如果不存在,并更新迁移历史表包括所有现有的迁移。

这里是类:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Data.Entity.Migrations.Infrastructure;
using System.Data.Entity.Migrations.Model;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Xml.Linq;

namespace EfStuff
{
    public class CreateDbWithMigrationHistoryIfNotExists<TContext, TMigrationsConfiguration> :
        IDatabaseInitializer<TContext>
        where TContext : DbContext
        where TMigrationsConfiguration : DbMigrationsConfiguration<TContext>
    {
        private readonly Regex _pattern = new Regex("ProviderManifestToken=\"([^\"]*)\"");
        private readonly TMigrationsConfiguration _config;

        public CreateDbWithMigrationHistoryIfNotExists()
        {
            _config = Activator.CreateInstance<TMigrationsConfiguration>();
        }

        public void InitializeDatabase(TContext context)
        {
            if (context.Database.Exists()) return;
            context.Database.Create();

            var operations = GetInsertHistoryOperations();
            if (!operations.Any()) return;
            var providerManifestToken = GetProviderManifestToken(operations.First().Model);
            var sqlGenerator = _config.GetSqlGenerator(GetProviderInvariantName(context.Database.Connection));
            var statements = sqlGenerator.Generate(operations, providerManifestToken);
            statements.ToList().ForEach(x => context.Database.ExecuteSqlCommand(x.Sql));
        }

        private IList<InsertHistoryOperation> GetInsertHistoryOperations()
        {
            return
                _config.MigrationsAssembly.GetTypes()
                       .Where(x => typeof (DbMigration).IsAssignableFrom(x))
                       .Select(migration => (IMigrationMetadata) Activator.CreateInstance(migration))
                       .Select(metadata => new InsertHistoryOperation("__MigrationHistory", metadata.Id,
                                                                      Convert.FromBase64String(metadata.Target)))
                       .ToList();
        }

        private string GetProviderManifestToken(byte[] model)
        {
            var targetDoc = Decompress(model);
            return _pattern.Match(targetDoc.ToString()).Groups[1].Value;
        }

        private static XDocument Decompress(byte[] bytes)
        {
            using (var memoryStream = new MemoryStream(bytes))
            {
                using (var gzipStream = new GZipStream(memoryStream, CompressionMode.Decompress))
                {
                    return XDocument.Load(gzipStream);
                }
            }
        }

        private static string GetProviderInvariantName(DbConnection connection)
        {
            var type = DbProviderServices.GetProviderFactory(connection).GetType();
            var assemblyName = new AssemblyName(type.Assembly.FullName);
            foreach (DataRow providerRow in (InternalDataCollectionBase) DbProviderFactories.GetFactoryClasses().Rows)
            {
                var typeName = (string) providerRow[3];
                var rowProviderFactoryAssemblyName = (AssemblyName) null;
                Type.GetType(typeName, (a =>
                                            {
                                                rowProviderFactoryAssemblyName = a;
                                                return (Assembly) null;
                                            }), ((_, __, ___) => (Type) null));
                if (rowProviderFactoryAssemblyName != null)
                {
                    if (string.Equals(assemblyName.Name, rowProviderFactoryAssemblyName.Name,
                                      StringComparison.OrdinalIgnoreCase))
                    {
                        if (DbProviderFactories.GetFactory(providerRow).GetType().Equals(type))
                            return (string) providerRow[2];
                    }
                }
            }
            throw new Exception("couldn't get the provider invariant name");
        }
    }
}


文章来源: How do I force Entity Framework to mark a particular migration as having been applied?