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



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

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

我运行了"Add-Migration MigrationXYZ -Force"以确保它不是一个问题,我放弃了DB,重新启动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看到的模型与实际情况不同,但是当你有依赖关系,如需要签名dll才能工作的Identity时,使用EF源是不可能的。

然而,额外的研究使我在这里找到了答案,这导致了@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,然后比较当前模型和迁移,建议还是模型不匹配的变化,应该不是什么零模型中还不空,而建议迁移将其显示为nullable。

预期的更改显示

<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之前运行上述我不确定)

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

update-database .

希望这将解决这个问题,它认为有一个额外的迁移

另一个选择,但它可能不是最好的解决方案是太手动编辑迁移和删除意外的部分

好吧,再看看@trailmax的答案,我想尝试一些东西,一个我没有包括在问题中的信息,并且因为它在其他地方使用而被驳回为原因,在这次迁移中没有改变,并且被驳回为@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属性,你猜怎么着,所有不应该存在的更改都消失了

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

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

update-database -connectionstring:" -provider:"

最新更新