C# 在具有EF Core 3.1的SQL Server上生成不可为null的行版本

C# 在具有EF Core 3.1的SQL Server上生成不可为null的行版本,c#,.net-core,entity-framework-core,ef-code-first,ef-fluent-api,C#,.net Core,Entity Framework Core,Ef Code First,Ef Fluent Api,我们正在尝试使用Fluent API在具有EF Core 3.1的SQL Server上生成不可为null的rowversion列: public class Person { public int Id { get; set; } public byte[] Timestamp { get; set; } } public class PersonEntityConfiguration : IEntityTypeConfiguration<Person> {

我们正在尝试使用Fluent API在具有EF Core 3.1的SQL Server上生成不可为null的rowversion列:

public class Person
{
    public int Id { get; set; }
    public byte[] Timestamp { get; set; }
}

public class PersonEntityConfiguration : IEntityTypeConfiguration<Person>
{
    public void Configure(EntityTypeBuilder<Person> builder)
    {
        builder.HasKey(p => p.Id);

        builder.Property(p => p.Timestamp)
            .IsRowVersion()
            .IsRequired();
    }
}
公共类人物
{
公共int Id{get;set;}
公共字节[]时间戳{get;set;}
}
公共类PersonEntityConfiguration:EntityTypeConfiguration
{
公共void配置(EntityTypeBuilder)
{
HasKey(p=>p.Id);
builder.Property(p=>p.Timestamp)
.IsRowVersion()
.IsRequired();
}
}
当整个表都是新的时,此选项可以正常工作:

public partial class PersonMigration : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.CreateTable(
            name: "Persons",
            columns: table => new
            {
                Id = table.Column<int>(nullable: false)
                    .Annotation("SqlServer:Identity", "1, 1"),
                Timestamp = table.Column<byte[]>(rowVersion: true, nullable: false)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_Persons", x => x.Id);
            });
    }
}
公共部分类PersonMigration:迁移
{
受保护的覆盖作废(MigrationBuilder MigrationBuilder)
{
migrationBuilder.CreateTable(
姓名:“人”,
列:表=>new
{
Id=table.Column(可空:false)
.Annotation(“SqlServer:Identity”、“1,1”),
Timestamp=table.Column(rowVersion:true,null:false)
},
约束:表=>
{
表.PrimaryKey(“PK_Persons”,x=>x.Id);
});
}
}
但是,我们有时需要将rowversion添加到现有表中。在这种情况下,EF Core将生成无效的迁移:

public partial class PersonTimestampMigration : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.AddColumn<byte[]>(
            name: "Timestamp",
            table: "Persons",
            rowVersion: true,
            nullable: false,
            defaultValue: new byte[] {  });
    }
}
public分部类PersonTimestampMigration:迁移
{
受保护的覆盖作废(MigrationBuilder MigrationBuilder)
{
migrationBuilder.AddColumn(
名称:“时间戳”,
表:“人员”,
是的,
可为空:false,
默认值:新字节[]{});
}
}
上面生成的默认值在应用于数据库时将导致异常:

执行DbCommand(1ms)失败[Parameters=[],CommandType='Text',CommandTimeout='30']

ALTER TABLE[PERSONESS]ADD[Timestamp]行版本不为空默认0x

Microsoft.Data.SqlClient.SqlException(0x80131904):无法在数据类型为timestamp的列上创建默认值。表“人员”,列“时间戳”。
无法创建约束或索引。请参阅前面的错误

这是EF Core中已知的错误吗?这个问题可以通过手动从迁移中删除
defaultValue:newbyte[]{}
来解决,但是有没有办法禁止使用Fluent API生成默认值

这是EF Core中已知的错误吗

这肯定是bug/缺陷,但可能还不清楚,因为即使在最新的EF Core 5.0预览版中也会出现这种情况。或者是已知的,但优先级较低(对于他们)-您必须检查EF核心


已尝试显式添加
.HasDefaultValue(null)
.HasDefaultValueSql(null)
-没有任何帮助,因此唯一的选项是从迁移中手动删除
defaultValue:new byte[]{}
。好的方面是,当您这样做时,它会工作,并且即使表有现有记录,列也会成功创建和填充(这就是为什么EF Core通常为新的必需列添加了这样的
defaultValue
参数,但正如我们所看到的,对于
ROWVERSION
,不应该这样做).

SQL Server中的
ROWVERSION
数据类型始终由数据库引擎处理-因此,我相信您无法为其定义默认值-无论如何,这是为了什么?感知到的好处是什么?这不是EF Core错误-这是SQL Server功能。@marc_s:错误在于EF Core不应该生成默认约束,因为它知道值总是由数据库填充的
IsRowVersion
将属性配置为
ValueGeneratedOnAddOrUpdate
IsConcurrencyToken
。好的-这是一个有效点-但正如您自己所说的-因为您也知道-只是不要在模型中指定默认值!还有一点需要注意:命名此列
Timestamp
有点危险,不可取——毕竟,这仍然是一个保留的T-SQL关键字。。。。。尽可能避免在命名过程中出现任何潜在冲突!我没有指定默认值。当我使用
.IsRowVersion().IsRequired()
(在现有表上)配置属性时,EF Core错误地为我生成了它。如果有办法抑制这种违约,我的问题就解决了。公平点重新列名。