Комментарии 16
Я так понимаю, что автор пытается решить фундаментальную проблему распределения модели данных по модулям. Но через code-first. Тут есть ожидаемая проблемка.
Code-first не умеет в совместное владение одной структурой разными модулями.
Это значит, что при расшарке структуры (таблицы, схемы) между модулями, у вас получается несколько источников истины. И надо решать, какая истина истиннее. А иначе рассинхрон, дрейф, дрейф, кладбище...
Ну можно конечно держать в голове, какие структуры расшарены и где. И менять синхронно во всех местах одновременно. Но вопрос, а напаркуа такая автоматизация при ручном управлении?
Я эту проблему решил совершенно иначе. Следите за руками.
Выносим источник истины (модель данных) из кода в отдельный артефакт. Чтобы EF Core с этим работал, прикручиваем генератор кода. Модель целиковая, на всю БД. Или на все БД (если их несколько). А потом для каждого модуля задаётся видимость через partial generation.
Итого для каждого модуля генерируется свои классы модели, определяя границы видимости данных. Но при этом модель одна.
Самое интересное в поддержке такой схемы работы. Модель одна по определению и изменения вносятся сразу во все модули, которые используют затронутую структуру. И очень просто менять область видимости. Просто наставить нужных чекбоксов и всё.
Code-first не умеет в совместное владение одной структурой разными модулями.
Не понял что это означает, можно с примерами кода?
Это значит, что при расшарке структуры (таблицы, схемы) между модулями, у вас получается несколько источников истины. И надо решать, какая истина истиннее. А иначе рассинхрон, дрейф, дрейф, кладбище...
Не понял в чем проблема. Можно пример кода? Что нужно ограничить чтобы такого не было?
Итого для каждого модуля генерируется свои классы модели, определяя границы видимости данных. Но при этом модель одна.
В этой статье модель одна, но разбитая на модули. Модули могут как напрямую инкапсулировать свои данные, так и можно выносить общие куски и ссылаться на них.
Можете привести пример кода что вы имеете ввиду?
Не понял что это означает, можно с примерами кода?
Нельзя. Такие вещи не показываются кодом. Сначала словами и концептуально.
Я же вижу, что вы также видите ограничение code-first и интуитивно пришли к тому же самому решению: вынести модель в отдельный артефакт. Только у меня это описательный формат, а у вас это тот же код с динамической сборкой в единую модель. Ну просто потому что иначе миграции просто не будут работать.
Code-first подход вообще состоит в том, что в коде написана истина, какой должна быть табличка. Тулчейн подхватывает код и всякой чёрной магией вкатывает изменения в реальную БД. Так вот, когда код в разных модулях описывает одну и ту же табличку, то тогда и происходит слом парадигмы.
И мы по сути, решаем одну и ту же проблему. Но немного разными способами. Но с выделением модели в отдельный артефакт, не принадлежащий модулям аппа.
У вас таблица вынесена в отдельный модуль, у меня каждый модуль генерируется заново из общей модели.
Ваш подход требует подключения сборок данных к модулям аппа. При этом модули не могут содержать navigation property, потому что тогда сборки должны перекрёстно друг от друга зависеть. Когда делаешь внешний ключ, ты уже прописываешь в одном классе ссылку на другой. Навегейшен проперти - это ссылка с обратной стороны, на тот класс, который уже гарантированно ссылается на этот. Вот и приехали.
Сколько при этом ещё может возникнуть циклических зависимостей в сложной модели - я даже боюсь себе представить.
У меня есть единая модель. Из которой можно построить необходимое количество dbContext, хоть на каждый модуль отдельный контекст, хоть по два.
Code-first подход вообще состоит в том, что в коде написана истина, какой должна быть табличка. Тулчейн подхватывает код и всякой чёрной магией вкатывает изменения в реальную БД. Так вот, когда код в разных модулях описывает одну и ту же табличку, то тогда и происходит слом парадигмы.
Понял о чем вы говорите. В одной из следующей стей будет описан способ как это преодолеть.
Суть простая: Модуль, который не ссылается на другие модули и реализует IEntityTypeConfiguration<T> объявляется "владельцем T", что записывается в атрибуты сборки, а потом анализатор на уровне хоста проверяет что нет двух модулей-владельцев одного типа.
Ваш подходи интересен, но хотелось бы увидеть код. Без кода сложно судить.
Вы код чего именно хотите увидеть?
Код который описывает модель и код который получается на выходе генератора.
Модель - это XML формат. Код на выходе генератора ничем не отличается от того, что пишут руками в парадигме code-first. При этом генератор - это просто питоний скрипт, который можно править под свои нужды и заморочки своего проекта. Там не на что смотреть. Ну если что, в первом сообщении была ссылка на код, который получается от генератора-примера. Там что-то такое:
[Table("ReleaseFiles")]
public partial class ReleaseFile
{
[Key]
public int Id { get; set; }
public int VersionId { get; set; }
public int ChannelId { get; set; }
public string Checksum { get; set; }
public int FileSize { get; set; }
public string Filename { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime CreatedDate { get; set; }
public int FileType { get; set; }
[ForeignKey("VersionId")]
public Version Version { get; set; }
[ForeignKey("ChannelId")]
public ReleaseChannel Channel { get; set; }
}SQL JOIN
Часто приложение и не должно знать что там джойнится. Ему дают витрину и всё. А как она там внутри БД сделана не аппа дело.
Так можно довольно гибко настраивать внутреннюю структуру БД. Можно и материализованные вьюшки если приложение допускает некоторое отставание данных read model в CQRS
Часто приложение и не должно знать что там джойнится. Ему дают витрину и всё. А как она там внутри БД сделана не аппа дело.
Витрина это что? Представления в БД или другой код?
Нам ведь кроме проекций нужно еще и CRUD делать. Можно конечно убрать проблему на другой слой, но от этого проблема не исчезнет.
Можно и материализованные вьюшки если приложение допускает некоторое отставание данных read model в CQRS
Это все копирование данных, о котором написано в статье, со всеми недостатками.
Интересно, но вопрос к использованию IHostingStartup: как-то странно в контексте бд видеть зависимость от asp.
EF Core это часть ASP.NET по факту.
Было на заре .net core, сейчас это уже не так.
Ну формально вроде нет, а фактически EF Core внутри себя опирается на Logger, DI, MemoryCache, а тулы dotnet ef плохо работают без хоста. Просто раньше это все официально было частью ASP.NET, а теперь это какая-то отдельная прослойка, которая вроде как не BCL, распространяется через NuGet, но вроде как и не ASP.NET
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Модульная модель данных в EF Core