Созданные значения

Значения столбцов базы данных могут формироваться различными способами: первичные ключевые столбцы часто автоматически увеличивают целые числа, другие столбцы — по умолчанию или вычисленные значения и т. д. На этой странице подробно описаны различные шаблоны создания значений конфигурации с EF Core.

Значения по умолчанию

В реляционных базах данных столбец можно настроить со значением по умолчанию. Если строка вставляется без значения для этого столбца, будет использоваться значение по умолчанию.

Можно настроить значение по умолчанию для свойства.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .Property(b => b.Rating)
        .HasDefaultValue(3);
}

Можно также указать фрагмент SQL, используемый для вычисления значения по умолчанию:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .Property(b => b.Created)
        .HasDefaultValueSql("getdate()");
}

Вычисляемые столбцы

В большинстве реляционных баз данных столбец можно настроить так, чтобы его значение было вычислено в базе данных, как правило, с выражением, ссылающимся на другие столбцы:

modelBuilder.Entity<Person>()
    .Property(p => p.DisplayName)
    .HasComputedColumnSql("[LastName] + ', ' + [FirstName]");

В приведенном выше примере создается виртуальный вычисляемый столбец, значение которого вычисляются при каждом выборке из базы данных. Можно также указать, что вычисляемый столбец должен храниться (иногда называемый материализованным), что означает, что он будет вычислен при каждом обновлении строки и хранится на диске вместе с обычными столбцами:

modelBuilder.Entity<Person>()
    .Property(p => p.NameLength)
    .HasComputedColumnSql("LEN([LastName]) + LEN([FirstName])", stored: true);

Примечание

В EF Core 5,0 добавлена поддержка создания сохраненных вычислений столбцов.

Первичные ключи

В соответствии с соглашением Несоставные первичные ключи типа Short, int, long или GUID настраиваются так, чтобы иметь значения, формируемые для вставленных сущностей, если оно не предоставлено приложением. Поставщик базы данных обычно выполняет необходимую настройку. Например, числовой первичный ключ в SQL Server автоматически настраивается как столбец ИДЕНТИФИКАТОРов.

Дополнительные сведения см. в документации по ключам.

Явная настройка создания значений

Мы узнали, что EF Core автоматически настраивает создание значений для первичных ключей, но может потребоваться сделать то же самое для неключевых свойств. Можно настроить любое свойство так, чтобы его значение создавалось для вставленных сущностей следующим образом:

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public DateTime Inserted { get; set; }
}

Аналогично, для свойства можно настроить создание значения при добавлении или обновлении:

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
    public DateTime LastUpdated { get; set; }
}

Предупреждение

В отличие от значений по умолчанию или от вычисленных столбцов, не указывается способ создания значений. Это зависит от используемого поставщика базы данных. Поставщики баз данных могут автоматически настроить создание значений для некоторых типов свойств, но для других может потребоваться вручную настроить способ создания значения.

Например, на SQL Server, когда свойство GUID настроено в качестве значения, созданного при добавлении, поставщик автоматически выполняет клиентское создание значений, используя алгоритм для создания оптимальных последовательных значений GUID. Однако указание ValueGeneratedOnAdd свойства DateTime не будет действовать (см. раздел ниже для создания значения DateTime).

Аналогичным образом, свойства Byte [], настроенные как созданные при добавлении или обновлении и помеченные как маркеры параллелизма, настраиваются с типом данных rowversion, чтобы значения автоматически создавались в базе данных. Однако указание ValueGeneratedOnAdd не оказывает никакого влияния.

Примечание

В зависимости от используемого поставщика базы данных значения могут создаваться клиентом на стороне EF или в базе данных. Если значение создается базой данных, то EF может присвоить временное значение при добавлении сущности в контекст. Это временное значение затем будет заменено формируемым базой данных значением во время SaveChanges() . Дополнительные сведения см. в документах по временным значениям.

Создание значения даты и времени

Распространенным запросом является наличие столбца базы данных, содержащего дату и время первого добавления столбца (значение, созданное при добавлении), или Дата последнего обновления (значение, созданное при добавлении или обновлении). Так как для этого существуют различные стратегии, поставщики EF Core обычно не настраивают автоматическое создание значений для столбцов даты и времени. это необходимо сделать самостоятельно.

Метка времени создания

Настройка столбца даты и времени на создание метки времени создания строки обычно зависит от настройки значения по умолчанию с помощью соответствующей функции SQL. Например, на SQL Server можно использовать следующее:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .Property(b => b.Created)
        .HasDefaultValueSql("getdate()");
}

Убедитесь, что выбрана соответствующая функция, так как может существовать несколько (например, GETDATE() VS GETUTCDATE() ).

Отметка времени обновления

Хотя сохраненные вычисляемые столбцы кажутся хорошим решением для управления отметками времени последнего обновления, базы данных обычно не позволяют указывать такие функции, как GETDATE() в вычисляемом столбце. В качестве альтернативы можно настроить триггер базы данных для достижения того же результата:

CREATE TRIGGER [dbo].[Blogs_UPDATE] ON [dbo].[Blogs]
    AFTER UPDATE
AS
BEGIN
    SET NOCOUNT ON;

    IF ((SELECT TRIGGER_NESTLEVEL()) > 1) RETURN;

    DECLARE @Id INT

    SELECT @Id = INSERTED.BlogId
    FROM INSERTED

    UPDATE dbo.Blogs
    SET LastUpdated = GETDATE()
    WHERE BlogId = @Id
END

Сведения о создании триггеров см. в документации по использованию необработанного SQL в миграции.

Переопределение создания значения

Несмотря на то, что свойство настроено для создания значений, во многих случаях вы можете явно указать его значение. Будет ли это работать, зависит от конкретного настроенного механизма создания значений. Хотя можно указать явное значение, а не использовать значение столбца по умолчанию, то то же самое невозможно сделать с вычисляемыми столбцами.

Чтобы переопределить создание значения с помощью явного значения, просто задайте для свойства любое значение, которое не является значением CLR по умолчанию для данного типа свойства (для, для, null string для и 0 int Guid.Empty Guid т. д.).

Примечание

Попытка вставки явных значений в SQL Server УДОСТОВЕРЕНие завершается сбоем по умолчанию; Дополнительные сведения см. в этих документах.

Чтобы предоставить явное значение для свойств, которые были настроены в качестве значения, созданного при добавлении или обновлении, необходимо также настроить свойство следующим образом:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>().Property(b => b.LastUpdated)
        .ValueGeneratedOnAddOrUpdate()
        .Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Save);
}

Без создания значения

В отличие от конкретных сценариев, описанных выше, для свойств обычно не настроено создание значений. Это означает, что приложение всегда должно предоставлять значение, которое должно быть сохранено в базе данных. Перед добавлением в контекст это значение должно быть назначено новым сущностям.

Однако в некоторых случаях может потребоваться отключить создание значений, настроенное по соглашению. Например, первичный ключ типа int обычно неявно настраивается как создаваемый при добавлении значения (например, столбец идентификаторов в SQL Server). Это можно отключить следующими средствами:

public class Blog
{
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int BlogId { get; set; }

    public string Url { get; set; }
}