Проблема возникает при наличии стандартного значения у типа данных свойства. Вспомните, что стандартное значение для числовых типов составляет
0
, а для булевских —
false
. Если вы установите значение числового свойства в
0
или булевского свойства в
false
и затем вставите такую сущность, тогда инфраструктура EF Core будет трактовать это свойство как не имеющее установленного значения. При отображении свойства на столбец со стандартным значением используется стандартное значение в определении столбца.
Например, добавьте в класс
Car
свойство типа
bool
по имени
IsDrivable
. Установите в
true
стандартное значение для отображения свойства на столбец:
игнорируется (т.к. оно является стандартным значением для булевского типа) и будет применяться стандартное значение. Таким образом, значение для
IsDrivable
всегда будет равно
true
! Одно из решений предусматривает превращение вашего открытого свойства (и, следовательно, столбца) в допускающее
null
, но это может не соответствовать бизнес-потребностям.
Другое решение предлагается инфраструктурой EF Core, в частности, ее работой с поддерживающими полями. Вспомните, что если поддерживающее поле существует (и идентифицируется как таковое для свойства через соглашение, аннотацию данных или Fluent API), тогда для действий по чтению и записи EF Core будет использовать поддерживающее поле, а не открытое свойство.
Если вы модифицируете
IsDrivable
с целью применения поддерживающего поля, допускающего
null
(но оставите свойство не допускающим
null
), то EF Core будет выполнять чтение и запись, используя поддерживающее поле, а не свойство. Стандартным значением для булевского типа, допускающего
null
, является
null
— не
false
. Описанное изменение обеспечит ожидаемое поведение свойства:
public class Car
{
...
private bool? _isDrivable;
public bool IsDrivable
{
get => _isDrivable ?? true;
set => _isDrivable = value;
}
Для информирования EF Core о поддерживающем поле используется Fluent API:
modelBuilder.Entity<Car>(entity =>
{
entity.Property(p => p.IsDrivable)
.HasField("_isDrivable")
.HasDefaultValue(true);
});
На
заметку! В приведенном примере вызов метода
HasField
не обязателен, потому что имя поддерживающего поля следует соглашениям об именовании. Он включен в целях демонстрации применения Fluent API для указания поддерживающего поля.
Исполняющая среда EF Core транслирует поле в показанное ниже определение SQL:
CREATE TABLE [dbo].[Inventory](
...
[IsDrivable] [BIT] NOT NULL,
...
GO
ALTER TABLE [dbo].[Inventory] ADD DEFAULT (CONVERT([BIT],(1)))
FOR [IsDrivable]
GO
Вычисляемые столбцы
Столбцы также могут вычисляться на основе возможностей хранилища данных. Для SQL Server есть два варианта: вычислять значение, основываясь на других полях в той же самой записи, либо использовать скалярную функцию. Скажем, чтобы создать в таблице
В версии EF Core 5 появилась возможность сохранения вычисляемых значений, так что значение вычисляется только при создании или обновлении строки. Хотя в SQL Server упомянутая возможность поддерживается, она присутствует не во всех хранилищах данных, поэтому проверяйте документацию по своему поставщику баз данных:
Чтобы определить отношение "один ко многим" с помощью Fluent API, выберите одну из сущностей, подлежащих обновлению. Обе стороны навигационной цепочки устанавливаются в одном блоке кода:
modelBuilder.Entity<Car>(entity =>
{
...
entity.HasOne(d => d.MakeNavigation)
.WithMany(p => p.Cars)
.HasForeignKey(d => d.MakeId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_Inventory_Makes_MakeId");
});
Если вы выберете в качестве основы для конфигурации навигационной сущности главную сущность, тогда код будет выглядеть примерно так: