Я наткнулся на эту статью, в которой объясняются принципы чистой архитектуры. Я хочу применить эти принципы в ASP.NET Core + EF. Я застрял на этой части:
Уровень приложения зависит только от абстракций, определенных в интерфейсы, и эти интерфейсы реализованы на внешних уровнях. Для например, проблемы с сохранением (например, сохранение элемента в базе данных) определяются только с точки зрения требований; логика персистентности в уровень инфраструктуры реализует эти требования.
Звучит вполне разумно: уровень приложения не должен знать, что EF — это вещь, а должен предоставлять только интерфейс, который затем будет реализован контекстом БД на уровне инфраструктуры.
Однако, когда я пытаюсь это сделать, я сталкиваюсь с блокировщиком. Скажем, мой контекст БД выглядит так:
public class DbContext {
public DbSet<Person> Persons {get; set;} = null!;
}
Согласно «Чистой архитектуре», DbContext теперь должен реализовать IDbContext, расположенный на уровне приложения:
public interface IDbContext {
public DbSet<Person> Persons {get; set;}
}
Однако DbSet — это тип, определенный в EF, поэтому уровень приложения также должен зависеть от EF, что, согласно моему пониманию статьи и «Чистой архитектуры», противоречит принципам.
В статье также есть ссылка на репозиторий, и когда я смотрю на зависимости уровня приложения, я обнаруживаю, что они фактически сделали уровень приложения зависимым от EF.
Есть идеи, как выйти из этого и добиться уровня приложения, независимого от уровня инфраструктуры и EF?
Я думал об этом, но IQueryable<Person> не имеет Add (в отличие от DbSet), и при проверке метод Add не исходит из каких-либо интерфейсов, а реализуется непосредственно в DbSet, поэтому я, к сожалению, до сих пор не вижу, как это сделать. сделать эту работу.
Если вам абсолютно необходимо абстрагироваться от Entity Framework, возможно, вам стоит изучить шаблон репозитория: Learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions/…
@TheHvidsten Мне это «абсолютно не нужно», я учусь, поэтому мне интересно, может ли блестяще выглядящая теория работать на практике, чему сама статья немного противоречит, ссылаясь на репозиторий, который демонстрирует то, о чем в статье не говорится: - Д. Спасибо за ссылку, почитаю немного
@TheHvidsten эта ссылка говорит совсем не об этом. То, что вы предложили, является уродливым антипаттерном. DbContext — это высокоуровневая единица работы с несколькими объектами, а не соединение с базой данных. Помещение поверх него низкоуровневого CRUD-класса полностью нарушает его функциональность как в качестве ORM, так и в качестве отключенного UoW.
@PanagiotisKanavos Я согласен, что это антишаблон, но нежелание правильно использовать Entity Framework не дает много хороших вариантов, поэтому, если вам абсолютно необходимо, то есть много плохих вариантов (отсюда и «абсолютно необходимо»).





Существуют разные подходы (с разными потенциальными недостатками):
Используйте интерфейсы IQueryable вместо DbSet - хотя это все равно будет дырявая абстракция (например, такие методы, как Include, специфичны для EF, поэтому вам нужно будет ссылаться на них, когда они понадобятся)
Не раскрывайте контекст как часть уровня вашего домена/приложения вообще V1:
Используйте репозитории (и/или единицы работы) поверх EF Core. Обратите внимание, что этот подход (или некоторые его варианты) можно рассматривать как антипаттерн (например, ).
Не раскрывайте контекст как часть вашего уровня домена/приложения вообще V2:
Используйте шаблон CQRS (или что-то подобное), когда на уровне домена/приложения запросы и команды будут объявлены как интерфейсы и будут реализованы на уровне инфраструктуры через EF.
Я полностью против размещения классов CRUD поверх DbContext, а вовсе не против правильных репозиториев доменов с несколькими объектами. Однако в случае с ORM, такими как EF, они в конечном итоге становятся тонкими оболочками и контейнерами для запросов. С другой стороны, однообъектные CRUD-классы, которые люди называют «универсальными репозиториями», обычно начинают с нарушения параллелизма, затем реализуют методы, вызывающие удаления, а затем люди приходят сюда и спрашивают, почему их код не работает.
Спасибо за обзор! Если чистая архитектура вокруг EF на практике не достижима, как практически решается эта проблема? Просто игнорировать чистую архитектуру и связать уровень приложения с EF, он же поставщик персистентности?
@karlosss TBH в своих приложениях (если только они не «простые») я склонен использовать EF Core в качестве инструмента уровня «DAL», используемого внутри репозитория, или абстракций CQRS, которые довольно хорошо сочетаются с чистой архитектурой. Конечно, бывают случаи, когда абстракция становится дырявой (например, когда нужно реализовать какую-то сложную логику в транзакционном контексте), но обычно в моих проектах это случалось не так часто. Также обратите внимание, что всегда есть «скрытый» 4-й вариант — шаблоны — это общие рекомендации, а не законы, их можно нарушать.
@GuruStron, случайно, есть ли у вас публичный репозиторий одного из этих приложений, где можно было бы продемонстрировать использование, где я мог бы «получить вдохновение»? Я думаю, что просмотр настоящего работающего кода поможет мне многое понять. Прежде чем нарушить шаблон, я хочу быть уверен, что у меня есть веские причины для этого, потому что, по моему опыту, нарушение шаблонов обычно не заканчивается хорошо :)
«Чистая архитектура» — это название книги, а не образец. Он превратился в общий маркетинговый термин для обозначения связанных идей и книг. Обычное описание узоров — A solution to a problem in a context. То, что работает для конкретной проблемы в одной ситуации (контексте), не обязательно является лучшим решением в другой ситуации. Как правило, это будет очевидно, когда вам понадобятся отдельные модели для EF, уровня приложения и… вашей модели ресурсов REST и API DTO. Хорошая модель базы данных не всегда является хорошим API DTO. Слой Abstractions, используемый для обоих, будет ломаться по мере роста приложения.
Например, шаблон размещенного проекта Blazor создает проект Shared, в который вы помещаете объекты, используемые как сервером, так и клиентом Blazor WASM (GUI). Это очень полезно до тех пор, пока... вам не понадобится добавлять аннотации для добавления проверки в графический интерфейс, что, в свою очередь, влияет на EF. Или осознать, что то, что Required в одной форме, нет Required в другой. Именно так были созданы ViewModels, чтобы удовлетворить потребности различных представлений вместо того, чтобы строить весь графический интерфейс поверх одной модели приложения. Если вы используете OData или GraphQL, аннотации также повлияют на API.
@karlosss "случайно, у вас есть публичный репозиторий" - извините, но нет, у меня нет ничего общедоступного в этой области, есть некоторые общедоступные, сделанные другими, но у многих из них та же проблема - они показывают очень кратко счастливый путь, который немного запутывает угловые случаи (но, с другой стороны, дает общее представление). Также вы можете просмотреть шаблоны ABP, которые имеют аналогичный подход AFAIK.
посмотрю, спасибо!
Вы можете определить интерфейс, который предоставляет IQueryable<Person>.