EF 6 CodeFirst Add-Migration支持并生成不存在的更改

时间:2016-08-18 16:05:55

标签: c# entity-framework ef-migrations

我在当前项目中使用EF Migrations已经有一段时间了,而且一切都运行良好,直到今天,情况如下:

  1. 我对添加字符串属性进行了一些小改动
  2. 我调用了一个API方法,并且出现了模型中存在更改的错误
  3. 我运行了命令“Add-Migration MigrationXYZ”
  4. 使用未发生的额外更改创建新迁移
  5. 我运行了“Add-Migration MigrationXYZ -Force”以确保它不是一件事,我放弃了数据库,重新启动了VS(2015),但都完全相同

    另一个问题是,即使我按照脚手架的方式应用迁移,仍然会返回错误“无法更新数据库以匹配当前模型,因为存在待定更改...”

    在查看这些更改之后,除了一个之外的所有更改都是关于具有[Required]属性的字符串属性,并且脚手架需要使其可为空,下面是一个示例。

    public partial class MigrationXYZ: DbMigration
    {
        public override void Up()
        {
            AddColumn("dbo.Foos", "NewProperty", c => c.String());//<-- Expected Change
            AlterColumn("dbo.Bars", "Name", c => c.String());//<-- Unexpected Change
        }
    
        public override void Down()
        {
            AlterColumn("dbo.Bars", "Name", c => c.String(nullable: false));//<-- Unexpected Change
            DropColumn("dbo.Foos", "NewProperty");//<-- Expected Change
        }
    }
    
    public class Bar
    {
        //This was not touched in ages, some even before adding the first migration
        [Required]
        public string Name { get; set; }
    }
    

    现在我陷入困境,不知道如何解决这个问题......移民状态下的腐败

    修改

    我一直在尝试调试Add-Migration命令,以了解为什么EF看到模型与实际不同,但是当你有像Identity这样需要签名的DLL工作的依赖时,使用EF源是不可能的。

    然而,另外的研究引导我进入answer here导致this blog post通过@trailmax和解密迁移哈希的代码,并在EF源代码中进行了一些搜索,我做了一个小应用程序提取当前模型和最后一个迁移模型,进行左右比较。

    以XML格式获取当前模型表示的代码

    //Extracted from EF Source Code
    public static class DbContextExtensions
    {
        public static XDocument GetModel(this DbContext context)
        {
            return GetModel(w => EdmxWriter.WriteEdmx(context, w));
        }
    
        public static XDocument GetModel(Action<XmlWriter> writeXml)
        {
            using (var memoryStream = new MemoryStream())
            {
                using (var xmlWriter = XmlWriter.Create(
                    memoryStream, new XmlWriterSettings
                    {
                        Indent = true
                    }))
                {
                    writeXml(xmlWriter);
                }
    
                memoryStream.Position = 0;
    
                return XDocument.Load(memoryStream);
            }
        }
    }
    
            //In Program.cs
            using (var db = new DbContext())
            {
                var model = db.GetModel();
                using (var streamWriter = new StreamWriter(@"D:\Current.xml"))
                {
                    streamWriter.Write(model);
                }
            }
    

    从XML迁移中提取模型的代码

    //Code from Trailmax Tech Blog
    public class MigrationDecompressor
    {
        public string ConnectionString { get; set; }
    
        public String DecompressMigrationFromSource(IMigrationMetadata migration)
        {
            var target = migration.Target;
            var xmlDoc = Decompress(Convert.FromBase64String(target));
            return xmlDoc.ToString();
        }
    
        public String DecompressDatabaseMigration(String migrationName)
        {
            var sqlToExecute = String.Format("select model from __MigrationHistory where migrationId like '%{0}'", migrationName);
    
            using (var connection = new SqlConnection(ConnectionString))
            {
                connection.Open();
    
                var command = new SqlCommand(sqlToExecute, connection);
    
                var reader = command.ExecuteReader();
                if (!reader.HasRows)
                {
                    throw new Exception("Now Rows to display. Probably migration name is incorrect");
                }
    
                while (reader.Read())
                {
                    var model = (byte[])reader["model"];
                    var decompressed = Decompress(model);
                    return decompressed.ToString();
                }
            }
    
            throw new Exception("Something went wrong. You should not get here");
        }
    
        /// <summary>
        /// Stealing decomposer from EF itself:
        /// http://entityframework.codeplex.com/SourceControl/latest#src/EntityFramework/Migrations/Edm/ModelCompressor.cs
        /// </summary>
        private XDocument Decompress(byte[] bytes)
        {
            using (var memoryStream = new MemoryStream(bytes))
            {
                using (var gzipStream = new GZipStream(memoryStream, CompressionMode.Decompress))
                {
                    return XDocument.Load(gzipStream);
                }
            }
        }
    }
    
            //Inside Program.cs
    
            var decompresser = new MigrationDecompressor
            {
                ConnectionString = "<connection string>"
            };
    
            var databaseSchemaRecord = decompresser.DecompressDatabaseMigration("<migration name>");
            using (var streamWriter = new StreamWriter(@"D:\LastMigration.xml"))
            {
                streamWriter.Write(databaseSchemaRecord);
            }
    

    不幸的是我仍然找不到问题,模型与上一次迁移之间的唯一区别是添加属性的预期更改,没有出现任何意外更改,也是在运行EF建议的迁移之后,然后将当前模型与建议的迁移进行比较,仍然模型与更改不匹配,模型中应该不为null的值仍然不为空,而建议的迁移将其显示为可为空。

    预期的更改会显示

    <Property Name="NewProperty" Type="String" MaxLength="Max" FixedLength="false" Unicode="true" />
    .
    .
    .
    <ScalarProperty Name="NewProperty" ColumnName="NewProperty" />
    .
    .
    .
    <Property Name="NewProperty" Type="nvarchar(max)" Nullable="true" />
    

3 个答案:

答案 0 :(得分:0)

尝试使用

将数据库回滚到之前的数据库

Update-database -targetMigration "nameofpreviousmigration"

(你可能需要在运行上面的内容之前运行update-database我不确定)

然后删除新迁移,创建全新迁移并运行

update-database

希望这会解决它认为存在额外迁移的问题

另一种选择,但可能不是最好的解决方案是手动编辑迁移并取出意外的部分

答案 1 :(得分:0)

好吧,再看看@ trailmax&#39; answer,我想尝试一些事情,这个信息我没有包含在这个问题中,并且因为它被用于其他地方,在这次迁移中没有改变,并且也被@trailmax解雇为原因,具体是属性和ExpressiveAnnotations属性。

我的实际Bar类看起来像这样

public class Bar
{
    //This was not touched in ages, some even before adding the first migration
    [Required]
    [AssertThat(@"<Condition>", ErrorMessage = "Please revise the name")]
    public string Name { get; set; }
}

我评论了AssertThat属性,然后猜测,不应该存在的所有变化都消失了。

答案 2 :(得分:-1)

请尝试使用update-database命令显式提供connectionstring和provider。您可以在connectionstring中找到这些值。

有时,我们可能需要指示实体框架连接到正确的数据库。其中一个案例是,选择错误的项目作为启动项目,这将使实体框架假设连接到默认数据库。

update-database -connectionstring:“” - 提供者:“”