Генерация ненулевой версии строки на SQL Server с EF Core 3.1

#c# #.net-core #entity-framework-core #ef-code-first #ef-fluent-api

#c# #.net-ядро #entity-framework-core #ef-code-first #ef-fluent-api

Вопрос:

Мы пытаемся сгенерировать ненулевой столбец rowversion на SQL Server с EF Core 3.1, используя Fluent API:

 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();
    }
}
  

Это отлично работает, когда вся таблица является новой:

 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);
            });
    }
}
  

Однако иногда нам нужно добавить версию строки в существующую таблицу. В этом случае 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[] {  });
    }
}
  

Сгенерированное выше значение по умолчанию вызовет исключение при применении к базе данных:

Не удалось выполнить DbCommand (1 мс) [Параметры=[], CommandType=’Text’, CommandTimeout=’30’]

ИЗМЕНИТЬ ТАБЛИЦУ [Persons] ДОБАВИТЬ [Timestamp] rowversion NOT NULL ПО УМОЛЧАНИЮ 0x;

Microsoft.Data.SqlClient.SQLException (0x80131904): значения по умолчанию не могут быть созданы для столбцов типа данных timestamp. Таблица ‘Persons’, столбец ‘Timestamp’.
Не удалось создать ограничение или индекс. Смотрите предыдущие ошибки.

Это известная ошибка в EF Core? Проблема может быть устранена путем ручного удаления defaultValue: new byte[] { } из миграции, но есть ли способ запретить генерирование значения по умолчанию с использованием Fluent API?

Комментарии:

1. ROWVERSION Тип данных в SQL Server всегда обрабатывается database engine — поэтому, я полагаю, вы не можете определить для него значение по умолчанию — зачем, в любом случае? В чем видимая выгода? Это не ошибка EF Core — это особенность SQL Server ..

2. @marc_s: Ошибка заключается в том, что EF Core не должен генерировать ограничение по умолчанию, поскольку он знает, что значение всегда заполняется базой данных. IsRowVersion настраивает свойство как ValueGeneratedOnAddOrUpdate и IsConcurrencyToken .

3. ХОРОШО — это правильный момент — но, как вы сами говорите — поскольку вы также это знаете — просто не указывайте значение по умолчанию в вашей модели! Также на заметку: присвоение имени этому столбцу Timestamp немного опасно и не рекомендуется — в конце концов, это все еще зарезервированное ключевое слово T-SQL ….. постарайтесь избежать любых потенциальных конфликтов в вашем именовании, если вы когда-либо сможете!

4. Я не указываю значение по умолчанию. EF Core неправильно генерирует ее для меня, когда я настраиваю свойство с .IsRowVersion().IsRequired() (в существующей таблице). Это решило бы мою проблему, если бы был способ подавить это значение по умолчанию. Справедливо указать имя столбца.

Ответ №1:

Это известная ошибка в EF Core?

Это наверняка ошибка / дефект, но, вероятно, неизвестно, поскольку это происходит даже в последнем на данный момент предварительном просмотре EF Core 5.0. Или известен, но с низким приоритетом (для них) — вы должны проверить отслеживание проблем в EF Core.

Пробовал добавлять явно .HasDefaultValue(null) и .HasDefaultValueSql(null) — ничего не помогает, поэтому единственный вариант — вручную удалить defaultValue: new byte[] { } из миграции. Хорошо то, что когда вы это делаете, это работает, и столбец создается и заполняется успешно, даже если в таблице есть существующие записи (по этой причине EF Core добавляет такой defaultValue аргумент для новых обязательных столбцов в целом, но, как мы видим, не следует этого делать для ROWVERSION ).