EF 6 CodeFirst 添加迁移脚手架并生成不存在的更改

EF 6 CodeFirst Add-Migration scaffoldes and generates changes that doesn't exist

我在当前的项目中使用 EF Migrations 有一段时间了,一切都很好,直到今天,情况如下:

  1. 我做了一个小改动,添加了一个字符串 属性
  2. 我调用了一个 API 方法并得到一个错误,指出模型发生了变化
  3. 我运行命令"Add-Migration MigrationXYZ"
  4. 创建了一个新的迁移,其中包含未发生的额外更改

我 运行 "Add-Migration MigrationXYZ -Force" 以确保这不是一回事,我删除了数据库,重新启动了 VS(2015),但都一样

另一个问题是,即使我按照脚手架所做的那样应用迁移,错误仍然returns说"Unable to update database to match the current model because there are pending changes..."

查看这些更改后,除了一个之外,其他所有更改都是关于具有 属性 和 [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 看到的模型与实际不同,但是当您具有需要签名 DLL 的 Identity 等依赖项时,无法使用 EF 源上班。

然而,额外的研究让我找到了 answer here which leads to 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);
        }

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

出现预期的变化

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

尝试使用

将您的数据库回滚到以前的数据库之一

Update-database -targetMigration "nameofpreviousmigration"

(您可能需要 运行 在 运行 更新数据库之前更新数据库,我不确定)

然后删除您的新迁移,创建一个全新的迁移,然后 运行

update-database.

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

另一种可能不是最好的解决方案是手动编辑迁移并删除意外部分

请尝试使用更新数据库命令明确提供连接字符串和提供程序。您可以在连接字符串中找到这些值。

有时,我们可能需要指示 entity framework 连接到正确的数据库。其中一种情况是,选择错误的项目作为启动项目,这将使 entity framework 假设连接到默认数据库。

更新数据库-connectionstring:"" -provider:""

好吧,再看看@trailmax 的 answer,我想尝试一些我没有包含在问题中的信息,并且因为它在其他地方使用而被排除在外,是在此迁移中未更改,并且也被@trailmax 排除为原因,具体是属性和 ExpressiveAnnotations 属性。

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

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属性,你猜怎么着,所有不应该存在的变化都消失了。