65.9K
CodeProject 正在变化。 阅读更多。
Home

EF6 迁移和时间戳错误,使用 CodeFirst

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2021年11月2日

CPOL

2分钟阅读

viewsIcon

7152

迁移不适用于 T-SQL 中 ALTER TABLE ALTER COLUMN 的规则

引言

在重构我们的 ASP.NET WEBAPI Odata 服务时,我们发现每个模型中返回四个字段。其中一个是 ModificationDate 属性,它是一个 TimeStamp 字段。因此,当从模型中删除这四个字段,并通过基类将它们添加到模型中时,迁移会做一些非常愚蠢的事情。它会为你的 TimeStamp 字段创建一个 AlterColumn 语句。当你从包管理器控制台运行 Update-Database 命令时,你会看到一个漂亮的错误

Cannot alter column 'ModificationDate' to be data type timestamp.

但是 Microsoft SQL Server 的文档 非常清楚。

ALTER COLUMN
指定要更改或修改的命名列。有关更多信息,请参阅 sp_dbcmptlevel (Transact-SQL)

修改后的列不能是以下任何一种

  • 具有时间戳数据类型的列。

这是迁移中的一个 BUG!

背景

基类中的 ModificationDate 属性的设置如下

/// <summary>
/// Gets or sets the modification date.
/// </summary>
/// <value>
/// The modification date.
/// </value>
[Timestamp]
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public byte[] ModificationDate { get; set; }

如何解决

在创建的 Add-Migration <YourPickedName> 中,你必须执行以下操作。

你在 Up() 方法中的行将是

AlterColumn("dbo.YourTable", "ModificationDate", 
c => c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "rowversion"));

你将其更改为

DropColumn("dbo.YourTable", "ModificationDate");
AddColumn("dbo.YourTable", "ModificationDate", 
c => c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "rowversion"));

因此,硬删除并重新创建是解决方案。

下一个问题是,我们必须为超过 300 个表更改它,因此我们在服务中创建了代码,在创建迁移时,如果我们将 ModificationDate 作为 fieldname,则会创建删除和添加列。

不幸的是,System.Data.Entity.Core.Metadata.Edm.PrimitiveTypeKind 具有 Byte ,但没有可用的 TimeStampRowVersion。否则,你可以通过其 DataType (alterColumnOperation.Column.Type) 来修复此问题。

如何自动化

在你的项目中,你有一个 Migrations 文件夹,你可以在其中添加一个类 MyCodeGenerator

using System.Data.Entity.Migrations.Design;
using System.Data.Entity.Migrations.Model;
using System.Data.Entity.Migrations.Utilities;
 
namespace MyHappyService.Migrations
{
    /// <summary>
    /// 
    /// </summary>
    /// <seealso cref="System.Data.Entity.Migrations.Design.CSharpMigrationCodeGenerator" />
    internal class MyCodeGenerator : CSharpMigrationCodeGenerator
    {
        /// <summary>
        /// Generates the specified alter column operation.
        /// </summary>
        /// <param name="alterColumnOperation">The alter column operation.</param>
        /// <param name="writer">The writer.</param>
        protected override void Generate
        (AlterColumnOperation alterColumnOperation, IndentedTextWriter writer)
        {
            if (alterColumnOperation.Column.Name == "ModificationDate")
            {
                DropColumnOperation dropColumnOperation = new DropColumnOperation
                    (alterColumnOperation.Table, alterColumnOperation.Column.Name);
                AddColumnOperation addColumnOperation = new AddColumnOperation
                   (alterColumnOperation.Table, alterColumnOperation.Column);
                base.Generate(dropColumnOperation, writer);
                base.Generate(addColumnOperation, writer);
            }
            else
                base.Generate(alterColumnOperation, writer);
        }
    }
}

在你的 Configuration.cs 中,你在构造函数中添加

CodeGenerator = new MyCodeGenerator();

下次你添加新的迁移时,此问题将为你解决。

祝您编码愉快!

历史

  • 2021年11月2日:Init v0.1:首次撰写此问题
© . All rights reserved.