4 / 4 / 2
Регистрация: 29.10.2015
Сообщений: 76
1

Так куда все таки внедрять EF DbContex (или репозитории)? в контроллеры или в модели представления?

25.12.2016, 12:41. Показов 1901. Ответов 16
Метки нет (Все метки)

Всем привет!
Прочитал кучу информации о подходе MVС в разработке пользовательских приложений и так пока и не смог понять одну вещь применительно к ASP.NET MVC:
в частности например используя Entity Framework, где все таки должен находится и использоваться DbContext (ну или репозитории-обёртки)? В контроллерах или в моделях представления? Где вызывается слой доступа к данным да и вообще бизнес-логика?

Почти во всех встречавшихся мне обучающих примерах и больших опенсорс проектах (например nopCommerce) DbContex`ы (или Repository<T> или в с случае если используется Анемик-модель как в nopCommerce, то сервисы, а репозитории уже в них) создавались именно в контроллерах и бизнес-логика по наполнению моделей представления данными также вызывается в контроллерах.

Вот небольшой пример контроллера этого случая, как во многих обучающих примерах (только обычно контекст в репозиториях, но не важно):
1 вариант (бизнес-логика в контроллере):
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
...
IDbContext DataContext { get; set; }
...
public ViewResult ProductList(string category, int page = 1)
{
            ProductPreviewModel model = new ProductPreviewModel
            {
               // наполним список продуктов модели представления (бизнес-логика в контроллере?)
               Products = DataContext.Set<Product>()
                  .Include(p => p.PreviewProductPhoto.Photo)
                  .OrderBy(p => p.ProductId)
                  .Skip((page -1)* itemsPerPage)
                  .Take(itemsPerPage)
                  .Select(p => new ProductPreviewModel()
                  {
                       Name = p.Name,
                       PreviewFotoUrl = p.PreviewProductPhoto.Photo.PreviewPhotoUrl,
                       Price = p.Price,
                       SeoUrl = p.SeoUrl
                  }).ToList();
            };
 
            // или бывает вот так выносят логику наполнения модели в отдельный NonAction-метод контроллера
            PrepareProductPreviewModel(model, category);
 
            return View(model);
}
 
 
PrepareProductPreviewModel(ProductPreviewModel model, string category) { ... };
 ...



И вроде бы все ничего, но вот другая немалая часть народа говорят (и в частности Википедия), что контроллер должен быть тонким и только передавать запросы и данные в модель, ругая тех, кто логику размещает в контроллерах, вот посмотрите:

Начинающие программисты (особенно в веб-программировании, где аббревиатура «MVC» стала популярна) очень часто трактуют архитектурную модель MVC как пассивную модель MVC: модель выступает исключительно совокупностью функций для доступа к данным, а контроллер содержит бизнес-логику. В результате — код моделей по факту является средством получения данных из СУБД, а контроллер — типичным модулем, наполненным бизнес-логикой (см. «скрипт» в терминологии веб-программирования). В результате такого понимания — MVC-разработчики стали писать код, который Pádraic Brady (известный в кругах сообщества «Zend Framework») охарактеризовал как «ТТУК» («Толстые, тупые, уродливые контроллеры»; Fat Stupid Ugly Controllers)[7]:

Как я понимаю если выносить бизнес-логику в модель, то методы контроллеров должны быть короткими:

2 вариант (бизнес-логика в модели):
C#
1
2
3
4
5
6
7
8
9
IDbContext DataContext { get; set; }
...
public ViewResult CategoryOverview(string categorySeoUrl = "root", int page = 1)
{
     var model = new CategoryOverviewModel(DataContext);
 
     model.LoadPagingCategoryProducts(categorySeoUrl, page, PageSize);
     return View(model);
}


Мнения у всех расходятся, в теории одно, на практике в обучающих примерах другое.
Я недавно обучаюсь ASP.NET MVC и не могу понять как правильнее поступать, в WinForms и Wpf я всегда DbContext использовал именно как член каждой модели представления (или базового класса) и логика была частью модели представления и сервисы внедрялись в модель представления, а тут обучаюсь и кругом вижу одни примеры с логикой, репозиториями и сервисами в самих контроллерах.

Как вы думаете, какой подход все же более правильный? Применительно к ASP.NET MVC?
__________________
Помощь в написании контрольных, курсовых и дипломных работ здесь
1
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
25.12.2016, 12:41
Ответы с готовыми решениями:

Так что же все таки выбрать - ASP, JSP, Java, PHP, Perl или ColdFusion?
Смотрю я на тему 'JSP против ASP' (http://relib.com/forums/topic.asp?id=728381) и душа не...

win7 или все таки XP
купил вот нетбук acer aspire one 522 AMD dual core 1ghz 2 gb ddr 3 320 gb hdd стоит 7-ка...

Видеокарта или все-таки нет?
Уважуха всем спецам! Помогите, пожалуйста, разобраться с одним вопросом! Материнка ASUS Striker...

Заменить или все таки оставить?
Нормальная ли видеокарта NVIDIA GeForce 9500 GT или надо поставить другую?

16
2472 / 1920 / 346
Регистрация: 22.07.2011
Сообщений: 7,290
25.12.2016, 17:45 2
Оба примера выше плохие , достаточно поменять тип репозитория - что бывает не редко , и нужно будет переписывать все места с подобными вызовами , а если приложение не одно , и не только веб но и десктоп с общей логикой ? - на производстве это выливается в кучу денег и времени , когда каждую правку нужно заного тестировать , внедрять клиентам , обьяснять что программист индус и т.п , короче в книжках верно пишут.
0
4 / 4 / 2
Регистрация: 29.10.2015
Сообщений: 76
26.12.2016, 18:30  [ТС] 3
sau,
ну а как правильно-то лучше поступать можешь, пожалуйста, высказать свое мнение? Применительно к ASP.NET? Всю логику выносить в проект "ядро", а модели представления веб-проекта "ASP.NET MVC" наполнять классами из ядра? (это как раз сервисный слой получается?) Или что ты имел ввиду?

В книжках и интернете сплошь примеры, что внедрять репозитории надо в контроллеры (или в сервисные классы, а сами сервисные классы - в контроллеры).

Я просто мало с тестированием связан, и иногда не думаю о вещах с ракурса тестирования, но хочу научится поступать правильно с репозиториями и слоями.
0
71 / 66 / 29
Регистрация: 28.10.2013
Сообщений: 248
27.12.2016, 11:04 4
У самого примерно такая логика:
3 ключевых проекта: BLL (бизнес сервисы, разные вспомогательные классы вроде TransactionScope), DAL (сущности, контекст, репозитории, UoW), UI (собственно контроллеры, представления и т.п.)
В контроллеры через конструктор внедряются исключительно бизнес сервисы. Они в свою очередь через конструктор получают UoW (совсем изредка другие бизнес сервисы). UoW через конструктор получает контекст и делится им с репозиториями.
Таким образом мы везде работаем только с бизнес сервисами. Бизнес сервисы работают с репозиториями из UoW и изредка с другими бизнес сервисами. Репозитории внутри UoW работают с контекстом.

За всю эту цепочку внедрения через конструктор говорим спасибо IoC. UoW и контекст в зависимости от места запроса создаются единожды на запрос/поток/некий LifetimeScope.

Меня пока что устраивает)
0
4 / 4 / 2
Регистрация: 29.10.2015
Сообщений: 76
27.12.2016, 11:51  [ТС] 5
Lutk,
то есть "Модели Представления" проекта UI у вас Anemic-стиля и наполняются данными в самих контроллерах через сервисы?
А модульное тестирование применяется у вас? ваша схема годится под тестирование?

ну вот, у вас как во многих примерах, и вы используете сервисный слой, но получается-то контроллеры у вас "толстые"?
Или тот факт, что в контроллерах идёт вызов сервисных классов и наполнение Модели Представления, не является основанием считать контроллер "толстым"?

Я просто в случае с MVVM всегда внедрял сервисы и репозитории в активные модели представления, а тут в MVC получается, что все делают немного по-другому.

И кстати как у вас? Репозиторий обобщенного типа? на одну сущность один сервисный класс? или это все условно и как удобнее надо делать?
0
71 / 66 / 29
Регистрация: 28.10.2013
Сообщений: 248
28.12.2016, 09:34 6
ribastar, да, наполнение моделей происходит в контроллерах.

Модульное тестирование применяется, создается mock контекст и в путь.

Мое личное имхо, что наполнение модели это роль именно контроллера. В конце концов именно он передает ее представлению, иногда только он и может ее наполнить данными (входящий параметр ReturnUrl при логине), зачем же размазывать логику наполнения по коду?

Репозитории обобщенные. Однако не вижу ничего криминального, если для некоторых сущностей будут персональные интерфейс и реализация, лишь бы это было хоть как-то обосновано)

С бизнес сервисами немного сложнее..
Обычно это 1 сущность - 1 сервис, но как быть, если мы реализуем наследование? Поясню: есть балансовая операция (сумма, ссылка на пользователя, время выполнения). Это абстрактный класс, поля которого вынесены в одну таблицу. Далее идут потомки: балансовая операция пополнения средств (способ пополнения, аккаунт-источник пополнения, транзакция пополнения), балансовая операция вывода средств (способ вывода, аккаунт-назначение вывода, транзакция вывода, комиссия), балансовая операция перевода средств (ссылка на пользователя, которому переводим) и т.п. Каждый из потомков может обладать уникальными собственными полями, поэтому каждый потомок это + сущность и + таблица. Сколько в данном случае должно быть бизнес сервисов? Если следовать "правилу" - 4, однако я бы сделал 1)
И обратная ситуация: есть некая сущность, которая делится на подмножества основываясь лишь на одном поле рассматриваемой сущности. Для каждого типа сущности есть большой набор уникальных операций. Почему бы в таком случае не разделить один бизнес сервис на несколько?
Так что я бы все это назвал пожеланиями, но никак не жесткими правилами)
1
4 / 4 / 2
Регистрация: 29.10.2015
Сообщений: 76
28.12.2016, 13:55  [ТС] 7
Lutk, спасибо ситуация по-немногу проясняется,
еще один вопросик есть про репозиторий, как я понял их смысл в том, чтобы в случае чего источник данных было легко подменить, но тут возникает вопрос, как же с помощью репозитория выполнять красивые linq-запросы через EF-context?
Вот например есть linq-запрос:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
 Products = DataContext.Set<Product>()
                  .Include(p => p.PreviewProductPhoto.Photo)
                  .Where(p => p.Categories.Any(c => c.Id == categoryId))
                  .OrderBy(p => p.ProductId)
                  .Skip((page -1)* itemsPerPage)
                  .Take(itemsPerPage)
                  .Select(p => new ProductPreviewModel()
                  {
                       Name = p.Name,
                       PreviewFotoUrl = p.PreviewProductPhoto.Photo.PreviewPhotoUrl,
                       Price = p.Price,
                       SeoUrl = p.SeoUrl
                  }).ToList();
Как всё это обернуть в репозиторий?
Я бы с радостью выбрал вначале из репозитория все Products в виде типа IQueryable<Products>, и далее применил все этим методы (Include, Skip, Take, Select), но может ли репозиторий возвращать IQueryable<T>? Или это ломает саму концепцию репозитория?

Или необходимо каждый из методов (Include, Skip, Take, Select) как-то отдельно оборачивать в доп. параметры-делегаты методов репозитория?
Так поступают?:

C#
1
2
3
4
5
6
7
8
9
10
11
interface IRepository<T>
{
          // вернем всех
          IEnumerable<T> GetFullList();
 
 
          // в случае, когда есть Include, Where, Skip, Take, Select
          // так что ли примерно надо?
         IEnumerable<T> GetPartOfList<TReturn>(Func<T, bool> include, Func<T, bool> where, 
                                               Func<T, bool> skip, Func<T, bool> take, Func<T, TReturn> select)
}
0
71 / 66 / 29
Регистрация: 28.10.2013
Сообщений: 248
28.12.2016, 14:07 8
ribastar, по-моему репозиторий и должен возвращать разве что IQueryable, а уже бизнес сервисы уточнять и "выполнять" запрос.

C#
1
2
3
4
public IQueryable<T> GetQueryable()
{
    return _context.Set<T>().AsQueryable();
}
0
4 / 4 / 2
Регистрация: 29.10.2015
Сообщений: 76
28.12.2016, 14:38  [ТС] 9
А разве в таком случае репозиторий выполняет свою функцию, а именно "возможность замены источника данных"?
Или у репозиториев не только такой смысл?

Ну вот, например, захотел я брать данные из Elasticsearch или mongoDb, получается для них надо реализовывать возврат такого же типа IQueryable<T>?
Это вообще реально реализовать возврат типа IQueryable<T> от, например, из ElasticSearch или даже допустим из XML-файла? или это будет неоправданный расход времени? Или это легко сделать?
0
90 / 90 / 44
Регистрация: 20.04.2008
Сообщений: 331
28.12.2016, 15:14 10
Смысла в репозитории нет, если он возвращает IQueryable, который гвоздями к Entity Framework в вашем случае прибит
0
71 / 66 / 29
Регистрация: 28.10.2013
Сообщений: 248
28.12.2016, 16:08 11
С одной стороны да, нужна будет реализация IQueryable, которую сделать сложно) с другой стороны для чего-то более менее распространенного готовые реализации уже есть..

Руководствуюсь такой логикой:
1) Смена источника данных для более менее крупного проекта на источник без готовой поддержки IQueryable это не смешно;
2) Обобщенную реализацию репозиториев для пагинации, фильтрации и прочих, назовем их рядовыми, действий сделать легко, но как только речь зайдет о каких-то тонкостях отдельных сущностей (привет join) - начнут плодиться реализации отдельных репозиториев под эти сущности, горы методов и т.п.

Пока что я для себя сделал вывод, что плюсы от использования IQueryable перевешивают минусы.
0
Эксперт .NET
9341 / 6703 / 1082
Регистрация: 21.01.2016
Сообщений: 25,290
29.12.2016, 08:28 12
Использовать IQueryable вполне себе оправдано. Даже паттерн для этого выдумали - CQRS. И из репозитория IQueryable вполне себе может "торчать". Плохого в этом ничего не вижу.
0
4 / 4 / 2
Регистрация: 29.10.2015
Сообщений: 76
29.12.2016, 13:53  [ТС] 13
Ух, мнения как всегда разделились, кто-нибудь выскажетесь еще, можно ли по-вашему IQueryable возвращать из репозитория?

И если да, то скажите, зачем вообще использовать репозитории IQueryable, если, например, XML-файлом или Elasticsearch-ом, источник данных не получиться подменить? Почему бы не использовать DbContext напрямую в сервис-классах? ведь если изначально не планировать использовать XML-файлы или Elasticsearch как источник данных, то для многих других известных СУБД реализации Entity Framework уже существуют и всегда можно просто подменить реализацию сразу всего, например, IDbContext? (интерфейс, в котором DbContext) (хотя и в случае репозиториев так можно поступить, они же содержать контекст)

Или опять таки смысл именно в удобстве тестирования, чтобы подменять реализации репозиториев mock-объектами?
0
90 / 90 / 44
Регистрация: 20.04.2008
Сообщений: 331
29.12.2016, 14:27 14
Думаю, вы правы насчет того, что если возвращаешь IQueryable из репозитория, то тогда уже просто используй DbContext напрямую.
И если на такой репозиторий нужно писать mock, то он будет неполноценным без mock реализации IQueryable, а эту реализацию пойди попробуй напиши
0
Эксперт .NET
9341 / 6703 / 1082
Регистрация: 21.01.2016
Сообщений: 25,290
29.12.2016, 14:59 15
off, ну тут уж определиться нужно, для чего программа пишется: для написания тестов под неё или для решения реальных задач.

У нас на работе, крупный проект нашего отдела имеет репозитории из которых таки торчит IQueryble. Целесообразность такого решения спорна: у нас постоянные споры по этому поводу. Но приложение живёт и пашет. Хоть и похоже немного на Квазимодо (под капотом).

Вообще, мы сам репозиторий заворачиваем в другую обёртку - менеджер сущности (UserManager, SecurityManager и т.д) и вот именно его методы из контроллера и дёргаем. А сам менеджер уже может или методы репозитория дёргать или LINQ-запрос сотворять.
0
9 / 9 / 0
Регистрация: 14.04.2015
Сообщений: 85
05.01.2017, 23:38 16
Наткнулся на вашу тему и это самого повело на мысли, заинтересовался и сам теперь сижу читаю, спасибо
Нашёл классную штуку - onion-архитектура asp.net mvc 5 приложений (для тех, кто не понял, что за репозитории и т.д. читайте и поймёте)
Мне вот прям понравилось, и как это тестировать всё теперь тоже очень понятно стало
0
4 / 4 / 2
Регистрация: 29.10.2015
Сообщений: 76
09.01.2017, 11:05  [ТС] 17
Onion-архитектура asp.net mvc 5 приложений это интересно! Почитаю тоже на досуге, посмотрим на эту точку зрения.
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
09.01.2017, 11:05

Все таки интерфейс или как?
у меня такой вопрос как получается что при использовании ADOX создается именно объект интерфейса...

CMS или все таки язык
Добрый день. Верчусь кручусь, хочу научиться создавать сайты. Лучше научиться определенному CMS?...

Видеокарта или все таки мост?
Здравствуйте. Излагаю суть проблемы. При включении компьютера биос выдает звуковой сигнал, который...

Видеокарта или все таки монитор?
Всем привет, подскажите проблема в видеокарте или в мониторе ? На биосе, как не странно, их меньше

acer или все таки Toshiba???
ребята как думаете что брать? TOSHIBA Satellite L655-S5114...

Математика. Отношения или все-таки СОотношения?
Привет всем! Хотелось бы прояснить один момент в математике... Решаем пример №1: Дан...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
17
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.