Свойство навигации EF Core без отношений внешнего ключа

В моем приложении, использующем EF Core 8.0.7 с подходом «сначала код», у меня есть таблица Content, содержащая содержимое приложения на основе языка. Таблица Content ссылается на записи в родительских объектах через ParentId, причем каждая запись Content уникально идентифицируется комбинацией Key, ParentId и LanguageId.

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

public virtual ICollection<Content>? Contents { get; set; }

Вот мой класс модели Content:

public class Content : EntityBase
{
    [MaxLength(40)]
    public string Key { get; set; }
    [MaxLength(2000)]
    public string Value { get; set; }
    public Guid ParentId { get; set; }
    public Guid LanguageId { get; set; }
    [MaxLength(40)]
    public string ParentTable { get; set; }
    public virtual Language? Language { get; set; }
}

Учитывая, что EF Core управляет свойствами навигации через внешние ключи, как мне настроить эти отношения? Должен ли я настроить свойство навигации определенным образом, чтобы гарантировать, что записи Content правильно связаны с их родительскими объектами? Существуют ли какие-либо конкретные конфигурации или соглашения EF Core, которым мне следует следовать?

Примечание. Мне известны похожие сообщения по этой теме, но они не решили мою проблему.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Короткий ответ: Нет. Избегайте этого деномализованного дизайна. Хотя это выглядит просто и компактно, вы не можете использовать отношения FK между одной таблицей содержимого и другими связанными таблицами, и, аналогично, база данных также не может обеспечить ссылочную целостность. Это также худший вариант с учетом отсутствия или сложной индексации и, что более важно, того факта, что все записи контента помещаются в одну таблицу. Если у вас есть 1 миллион строк контента по 10 связанным объектам, запросы будут более производительными с выделенными FK и индексами по отдельным таблицам контента (100 тысяч здесь, 250 тысяч там, 25 тысяч для типа C и т. д.), чем постоянный запрос по 1 миллиону строк.

Лучше:

public abstract class Content : EntityBase
{
    [MaxLength(40)]
    public string Key { get; set; }
    [MaxLength(2000)]
    public string Value { get; set; }
    public Guid LanguageId { get; set; }
    [MaxLength(40)]
    public virtual Language? Language { get; set; }
}

public class CompanyContent : Content
{
    public virtual Company Company { get; set; }
}

public class Company : EntityBase
{
    // ...
    public virtual ICollection<CompanyContent> Contents { get; } = [];
}

Использование модели наследования TPC или TPT. Я склоняюсь к TPC для более простых и быстрых объединений, но TPT потенциально был бы более полезен, если вам необходимо выполнять запросы ко всему контенту, независимо от родства.

Не имеет значения, что в итоге вы получите в 10 раз больше таблиц, где каждая таблица отличается только от FK до поддерживаемого ею типа. «Цена» базы данных — это дополнительные определения таблиц, но стоимость хранения данных для общего количества строк остается прежней. Запрос содержимого для любой таблицы меньшего размера не предполагает опрос всего набора содержимого, а только строк содержимого, относящихся к этой таблице.

Большое спасибо, после того, как я углублюсь в это, я воспользуюсь этим подходом.

Devx3Fr 31.08.2024 01:38

Другие вопросы по теме