Работали ли вы с shadow properties (скрытыми свойствами) в Entity Framework?

Ответ

Да, работал. Shadow properties — это свойства, которые не определены в классе вашей модели, но существуют в модели EF Core и сопоставлены со столбцами базы данных.

Типичные сценарии использования:

  • Аудит: Автоматическое отслеживание CreatedDate и ModifiedDate.
  • Мягкое удаление: Флаг IsDeleted.
  • Внешние ключи (FK): Для отношений, где навигационное свойство есть, но явное свойство FK в классе не требуется.

Пример настройки и использования:

// В DbContext.OnModelCreating
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .Property<DateTime>("LastUpdated") // Определяем shadow property
        .HasDefaultValueSql("GETUTCDATE()");

    modelBuilder.Entity<Blog>()
        .Property<bool>("IsDeleted");
}

// Как получить или установить значение shadow property
var blog = context.Blogs.First();
// Получаем значение
var lastUpdated = context.Entry(blog).Property<DateTime>("LastUpdated").CurrentValue;
// Устанавливаем значение
context.Entry(blog).Property<bool>("IsDeleted").CurrentValue = true;

Ключевые моменты:

  • Доступ к ним осуществляется через DbContext.Entry(entity).Property<T>("PropertyName").
  • Они могут использоваться в запросах LINQ через статический метод EF.Property.
  • Полезны для инфраструктурных данных, которые не должны быть частью публичного API доменной модели.

Ответ 18+ 🔞

А, ну shadow properties, да, работал, конечно. Это такие свойства, которые в твоём классе модели нихуя не объявлены, но EF Core их в своей внутренней модели держит и в базу колонки под них рисует. Хитро, да? Как будто призраки какие-то.

Где эта хуйня пригождается:

  • Аудит: Чтобы не писать каждый раз руками, когда запись создали или обосрали. Автоматом проставляются CreatedDate и ModifiedDate.
  • Мягкое удаление: Чтоб не удалять нахуй, а просто выставлять флажок IsDeleted. Удобно, потом можно всё откатить, как будто ничего и не было.
  • Внешние ключи: Когда навигационное свойство у тебя есть, а вот явно хранить айдишник в классе — лень или некрасиво. EF Core сам тебе этот FK в тени создаст, маньяк скрытный.

Вот смотри, как это выглядит в деле:

// Ковыряемся в OnModelCreating
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .Property<DateTime>("LastUpdated") // Вот он, призрак! Объявляем прямо по имени строкой.
        .HasDefaultValueSql("GETUTCDATE()"); // И сразу дефолтное значение из базы прикрутили.

    modelBuilder.Entity<Blog>()
        .Property<bool>("IsDeleted"); // Ещё один невидимка для мягкого удаления.
}

// А вот как с этими призраками общаться, когда нужно
var blog = context.Blogs.First();
// Вытаскиваем значение, как контрабанду
var lastUpdated = context.Entry(blog).Property<DateTime>("LastUpdated").CurrentValue;
// Засовываем новое значение
context.Entry(blog).Property<bool>("IsDeleted").CurrentValue = true;

Важные моменты, чтоб не обосраться:

  • Доступ к ним — только через вот эту штуку DbContext.Entry(entity).Property<T>("PropertyName"). По-другому никак, они же в тени, блядь.
  • В LINQ-запросах их можно использовать, но через статический метод EF.Property. Иначе компилятор тебя не поймёт, он же про призраков не в курсе.
  • Вообще, идея в том, чтобы всякую служебную хрень, типа дат аудита или флагов удаления, не таскать в публичном API твоей модели. Пусть живут тихо, на задворках EF Core, не мозоля глаза. Удобная хуйня, если использовать с умом.