Форум программистов, компьютерный форум, киберфорум
C#: Базы данных
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.54/13: Рейтинг темы: голосов - 13, средняя оценка - 4.54
3 / 3 / 0
Регистрация: 07.11.2018
Сообщений: 119
MS SQL

Как красиво получить объект с подсчетом связанных данных

25.07.2022, 02:42. Показов 2567. Ответов 30

Студворк — интернет-сервис помощи студентам
Возьму для примера объект Album у которого связь с объектами Image.
Мне нужно на сайт вернуть альбомы с количеством фото внутри.
Я знаю как это сделать, но мне не нравятся мои варианты реализации.

Я использую [NotMapped] поля для дополнительных данных в объекте модели.
Первый вариант.
C#
1
2
3
4
5
6
7
8
9
10
var res = _context.Albums.Where(x => x.AccountId == id)
                           .Include(x => x.Images.Take(1))
                           .Select(x => new
                           {
                               Album = x,
                               ImagesCount = x.Images.Count
 
                           }).ToList();
            res.ForEach(x => x.Album.ImagesCount = x.ImagesCount);
            return res.Select(x => x.Album);
Здесь приходится 2 раза перебирать дополнительные поля.

Второй вариант
Перебрать все поля в новый объект, но учитывая массивность некоторых объектов я его не использую.

Третий вариант
Вернуть кортеж с 2 объектами (Альбом + Объект с деталями сущности)
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
var res = _context.Albums.Where(x => x.AccountId == id)
                           .Include(x => x.Images.Take(1))
                           .Select(x => new
                           {
                               Album = x,
                               Details = new AlbumDetails
                                             {
                                                   ImagesCount = x.Images.Count
                                                   /*Тут бывает много полей*/
                                             }
 
                           }).ToList();
            return (res.Select(x => x.Album), res.Select(x => x.Details));
Здесь уже нет 2 переборов и [NotMapped] полей, но появляется необходимость использовать AutoMapper 2 раза для превращения в DTO.

По итогу я бы хотел видеть реализацию наподобие этой, но увы это не сработает, так как IQueryable не поддерживает тело в Select.
C#
1
2
3
4
5
6
7
return _context.Albums.Where(x => x.AccountId == id)
                           .Include(x => x.Images.Take(1))
                           .Select(x =>
                           {
                               ImagesCount = x.Images.Count,
                               return x;
                           });
Как сделать то что мне нужно красиво?
0
Лучшие ответы (1)
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
25.07.2022, 02:42
Ответы с готовыми решениями:

Подключил базу данных sql как оформить красиво?
Ребят как сделать красиво таблицы которые я подключил к бд? у меня есть форма в которой я ввожу данные и 8 таблиц в которой данные...

Получить адрес на переменную\объект, потом от этого адреса создать указатель на объект и изменить значение. Как?
int a=55; uintptr_t adr=&a; int *b=*adr; *b=88; cout << "a=" << a << "b=" << b; Добавлено через 16 минут так...

PHP и вывод значений из базы данных как оформить красиво
значения из бд MySQL я знаю как выводить (но вывожу их строками - как в текстовом редакторе:D ) к примеу $rs = mysql_query($strSQL); ...

30
Эксперт .NET
 Аватар для Usaga
14313 / 9393 / 1355
Регистрация: 21.01.2016
Сообщений: 35,427
25.07.2022, 19:03
Лучший ответ Сообщение было отмечено Nuril как решение

Решение

Цитата Сообщение от Nuril Посмотреть сообщение
Перебрать все поля в новый объект
Это ЕДИНСТВЕННЫЙ корректный вариант.

Класс сущности не более, чем СХЕМА модели данных. Сущности нужны только для формирования запросов. Не надо в них никакие [NotMapped] свойства пихать или кортежи на их основе делать.

Вы когда голый SQL-запрос к базе будете писать, вы разве не указываете явно какие поля из каких таблиц взять?

Цитата Сообщение от Nuril Посмотреть сообщение
использовать AutoMapper
Зачем?
1
3 / 3 / 0
Регистрация: 07.11.2018
Сообщений: 119
26.07.2022, 08:31  [ТС]
Цитата Сообщение от Usaga Посмотреть сообщение
Зачем?
Image.Path -> ImageDto.Base64, дальше домапливал детали к альбому в 1 строку.

Цитата Сообщение от Usaga Посмотреть сообщение
Это ЕДИНСТВЕННЫЙ корректный вариант.
Я создал объекты типу AlbumWithDetails для моделей которые нуждаются в подобных запросах и возвращаю в них данные из репозитория.

Теперь код превратился из:
C#
1
2
3
4
5
6
7
8
9
public ActionResult ProfileByIdOrLogin(string id)
        {
            var cortage = db.Accounts.GetProfile(id); //тут был вариант с кортежом
            cortage.account.Albums = db.Albums.GetAlbums(cortage.account.Id, 2).ToList(); //тут с циклом.
            /*Тут ещё данные аккаунта*/
            var dto = _mapper.Map<AccountDto>(cortage.account);
            _mapper.Map(cortage.details, dto);
            return Ok(dto);
        }
В:
C#
1
2
3
4
5
6
7
8
public ActionResult ProfileByIdOrLogin(string id)
        {
            var accountWithDetails = db.Accounts.GetProfile(id); //AccountWithDetails
            var dto = _mapper.Map<AccountDto>(accountWithDetails);
            dto.Albums = _mapper.Map<AlbumDto[]>(db.Albums.GetAlbums(accountWithDetails.Id, 2).ToList()); //AlbumWithDetails
            /*Тут ещё данные аккаунта*/
            return Ok(dto);
        }
Так правильно?

Будет ли верным заменить поле Albums в AccountWithDetails на тип AlbumWithDetails, чтобы класть туда данные и превращать всё за 1 маппинг аккаунта?
0
Эксперт .NET
 Аватар для Usaga
14313 / 9393 / 1355
Регистрация: 21.01.2016
Сообщений: 35,427
26.07.2022, 09:01
Цитата Сообщение от Nuril Посмотреть сообщение
Image.Path -> ImageDto.Base64, дальше домапливал детали к альбому в 1 строку.
Я в целом о необходимости использовать Automapper. Он тут не нужен вовсе. Поверьте)
0
Эксперт .NET
 Аватар для Usaga
14313 / 9393 / 1355
Регистрация: 21.01.2016
Сообщений: 35,427
26.07.2022, 09:01
Цитата Сообщение от Nuril Посмотреть сообщение
Image.Path -> ImageDto.Base64, дальше домапливал детали к альбому в 1 строку.
Я в целом о необходимости использовать Automapper. Он тут не нужен вовсе. Поверьте)

Цитата Сообщение от Nuril Посмотреть сообщение
Так правильно?
Нет. Вы не оптимально работаете с данными.

Не db.Albums.GetAlbums(accountWithDetails.Id, 2).ToList(), а db.Albums.GetAlbums(accountWithDetails.Id, 2).Select(x => ...).ToList()

Выполняйте проекцию во время запроса. Заводите DTO под каждый запрос, где будут только те свойства, что нужны. Там же будут и свойства, которых нет в исходных сущностях. Только так.
0
 Аватар для Andrey-MSK
3354 / 2240 / 388
Регистрация: 14.08.2018
Сообщений: 7,574
Записей в блоге: 4
26.07.2022, 09:03
Nuril, а не проще написать запрос SQL и показать что вам нужно в итоге? Чего сидеть гадать...
0
3 / 3 / 0
Регистрация: 07.11.2018
Сообщений: 119
26.07.2022, 09:32  [ТС]
Цитата Сообщение от Usaga Посмотреть сообщение
.Select(x => ...)
Это имеется в виду вернуть IQueryable<Album> вместо IEnumerable<AlbumWithDetails>?
0
Эксперт .NET
 Аватар для Usaga
14313 / 9393 / 1355
Регистрация: 21.01.2016
Сообщений: 35,427
26.07.2022, 09:45
Nuril, да. Либо сразу в методе GetAlbums выполнить проекцию. И зачем вы итератор возвращаете, когда вам лист нужен?
0
3 / 3 / 0
Регистрация: 07.11.2018
Сообщений: 119
26.07.2022, 09:59  [ТС]
Цитата Сообщение от Usaga Посмотреть сообщение
И зачем вы итератор возвращаете, когда вам лист нужен?
Ошибка
Цитата Сообщение от Usaga Посмотреть сообщение
сразу в методе GetAlbums
Репозиторий же не должен возвращать Dto объекты.
0
Эксперт .NET
 Аватар для Usaga
14313 / 9393 / 1355
Регистрация: 21.01.2016
Сообщений: 35,427
26.07.2022, 10:17
Цитата Сообщение от Nuril Посмотреть сообщение
Репозиторий же не должен возвращать Dto объекты.
Он не должен возвращать сущности. Он должен манипулировать моделями и DTO предметной области. А сущность EF'а - это аспект Data Layer. Скрытый аспект. Сущности EF'а есть описание таблиц в базе. А про таблицы и базы ничто ВНЕ репозитория знать не должно.
0
1341 / 920 / 265
Регистрация: 08.08.2014
Сообщений: 2,768
26.07.2022, 13:33
Цитата Сообщение от Nuril Посмотреть сообщение
Репозиторий же не должен возвращать Dto объекты
У каждого слоя - свои DTO или модель данных.

Но репозиторий/датасервис - не должен наружу раскрывать ничего, что касается особенностей реализации хранилища данны:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public sealed class Some[DataService|Repository] : ISome[DataService|Repository]
{
    public IEnumerable<Some[Dto]> Get(<filter>)
    {
        //Здесь может быть:
        //  EF, Linq2db, Dapper
        //  ручной маппинг из SQL-запроса через DataReader
        //  получение данных из какого-то другого хранилища (файл, например)
        //  ещё что-то
        //Более того:
        //  метод получения данных может быть реализован через Dapper
        //  а метод обновления - через EF
        //  это всё внутренние особенности реализации репозитория/датасервиса
        //  снаружи же - только Some[Dto], который не зависит от этих особенностей
    }
}
При этом сущности EF - это, по сути, просто инструмент описания структуры БД для EF. И их совершенно точно не стоит отдавать в вышестоящие слои, порождая там лишние зависимости. И при материализации данных из БД в SomeDto, выполняться это должно напрямую в SomeDto, т.е. без промежуточной выгрузки в сущности EF.
2
3 / 3 / 0
Регистрация: 07.11.2018
Сообщений: 119
26.07.2022, 15:29  [ТС]
Цитата Сообщение от kotelok Посмотреть сообщение
У каждого слоя - свои DTO или модель данных.
То есть в репозитории я делаю проекцию в DTO уровня DAL которое хранит данные в виде сущностей бд, а после в контроллере превращаю в DTO верхнего уровня?
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Album
    {
        public int Id { get; set; }
        public string Name { get; set; } = string.Empty;
        public virtual ICollection<Image> Images { get; set; }
    }
 
public class Image
    {
        public int Id { get; set; }
        [Required]
        public string Path { get; set; } = string.Empty;
     }
Из репозитория я отдаю. (Или это лишнее?)
C#
1
2
3
4
5
6
public class AlbumDto
    {
        public int Id { get; set; }
        public string ImagesCount { get; set; } = string.Empty;
        public IEnnumerable<Image> Images { get; set; }
    }
Далее в контроллере я уже делаю такой обьект
C#
1
2
3
4
5
6
7
8
9
10
11
public class AlbumDto
    {
        public int Id { get; set; }
        public string ImagesCount { get; set; } = string.Empty;
        public IEnnumerable<ImageDto> Images { get; set; }
 
public class ImageDto
    {
        public int Id { get; set; }
        public string Base64 { get; set; } = string.Empty;
    }
0
1341 / 920 / 265
Регистрация: 08.08.2014
Сообщений: 2,768
26.07.2022, 15:30
Цитата Сообщение от Nuril Посмотреть сообщение
То есть в репозитории я делаю проекцию в DTO уровня DAL которое хранит данные в виде сущностей бд, а после в контроллере превращаю в DTO верхнего уровня?
Модель/DTO уровня DAL - это НЕ сущности БД. В частном случае они да, могут даже полностью совпадать по составу/типу полей, но фактически - это полностью независимая проекция части данных из какого-то хранилища.

И в вашем примере 'AlbumDto' не должен содержать в себе коллекцию 'Image' (это ведь в вашем случае EF-сущность?).
2
3 / 3 / 0
Регистрация: 07.11.2018
Сообщений: 119
26.07.2022, 15:36  [ТС]
Цитата Сообщение от kotelok Посмотреть сообщение
(это ведь в вашем случае EF-сущность?).
Создам ImageDto в DAL, но оставшееся использовать верно?
0
1341 / 920 / 265
Регистрация: 08.08.2014
Сообщений: 2,768
26.07.2022, 15:44
Цитата Сообщение от Nuril Посмотреть сообщение
Создам ImageDto в DAL, но оставшееся использовать верно?
Да.

Только не очень понимаю, зачем вам отдельное свойство "ImageCount" заводить (ещё и строкового типа). У вас ведь всё равно изображения в подчинённой коллекции есть в этом DTO. И вы их целиком материализуете. А значит и количество элементов коллекции вам известно.
0
 Аватар для Andrey-MSK
3354 / 2240 / 388
Регистрация: 14.08.2018
Сообщений: 7,574
Записей в блоге: 4
26.07.2022, 15:46
Nuril, Ну у вас же БД выступает моделью, зачем вам еще плодить кучу DTO? У вас ведь есть класс данных, который описывает таблицу или VIEW. Вот его и можно гонять по всему приложению, это ведь по сути и есть DTO, так как в нём нет никакой реализации логики, ну может если только ToString() переопределить. А модель, БД, будет неизменной со своей БЛ и т.д.
1
1341 / 920 / 265
Регистрация: 08.08.2014
Сообщений: 2,768
26.07.2022, 15:50
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
У вас ведь есть класс данных, который описывает таблицу или VIEW. Вот его и можно гонять по всему приложению, это ведь по сути и есть DTO
Подобное применимо далеко не всегда, т.е. это скорее редкий частный случай. И ещё это потребует реализовать EF-контекст исключительно через fluent-API и вынести классы EF-сущностей в отдельную сборку, дабы в классах EF-сущностей не было EF-атрибутов, которые будут тянуть зависимость от EF-библиотек во все слои.
0
3 / 3 / 0
Регистрация: 07.11.2018
Сообщений: 119
26.07.2022, 15:52  [ТС]
Цитата Сообщение от kotelok Посмотреть сообщение
(ещё и строкового типа)
Ошибка, там будет int

Цитата Сообщение от kotelok Посмотреть сообщение
У вас ведь всё равно изображения в подчинённой коллекции есть в этом DTO.
При получении альбома я ограничиваю фото внутри до 1 элемента, чтобы использовать его в качестве заставки альбома на сайте, а поверх заставки отображаю количество фото внутри тем полем.
0
1341 / 920 / 265
Регистрация: 08.08.2014
Сообщений: 2,768
26.07.2022, 15:53
Andrey-MSK
Т.е. по сути, у вас получается, что структура запросов/ответов внешнего API жёстко привязана к структуре данных хранилища (если использовать EF-сущности во всех слоях в качестве DTO). Т.е. добавили в базу какое-то вспомогательное системное поле - оно автоматом опубликовалось в API. Неудобно.
0
 Аватар для Andrey-MSK
3354 / 2240 / 388
Регистрация: 14.08.2018
Сообщений: 7,574
Записей в блоге: 4
26.07.2022, 15:56
kotelok, А что может потянуть класс данных в EF? Аннотации? Дак они не из из библиотек EF, они из System.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
 
namespace MEFCFDAL.Model
{
    [Table("tblEObject", Schema = "dbo")]
    public class EObject
    {
        [Column("ID_EObject", TypeName = "int"), Key]
        public int EObjectID { get; set; }
 
        [Column("EObjectName", TypeName = "nvarchar"), StringLength(200)]
        public string EObjectName { get; set; }
 
        public virtual ICollection<GPlan> GPlans { get; set; }
 
        public override string ToString()
        {
            return $"{EObjectName}";
        }
    }
}
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
26.07.2022, 15:56
Помогаю со студенческими работами здесь

Как получить DataTable из двух связанных таблиц?
У меня есть DataSet, включающий две связанные таблицы. Мне нужно получить на выходе DataTable - выборку из этих двух таблиц. Перерыл...

Как, используя рефлексию, получить перечень событий, связанных с той же кнопкой?
Не имя события- имя события и дурак получит. А само событие! Ну то есть: создадим форму и кинем на неё кнопку. using System; using...

Получить std::tm из time_point, или как избежать ошибок ODR, связанных с использованием time_t
Всем привет! Есть код: // функция не делает поправки на часовой пояс ::std::tm getGMT(const ::std::time_t time)...

Как сделать отображение определенных связанных данных в DataGridView2
Есть 2 таблицы table1 и table2. 1 в DataGridView1 в которой 2 колонки, к примеру ID и Name. Нужно чтоб при выборе в DataGridView1 ID с...

Как выполнить AR Запрос в Yii на выборку записей по значению связанных данных?
Добрый день, Есть 2 таблицы: m_bids ------------ id mc_points ------------


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

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
Автоматическое создание документа при проведении другого документа
Maks 29.03.2026
Реализация из решения ниже выполнена на нетиповых документах, разработанных в конфигурации КА2. Есть нетиповой документ "ЗаявкаНаРемонтСпецтехники" и нетиповой документ "ПланированиеСпецтехники". В. . .
Настройка движения справочника по регистру сведений
Maks 29.03.2026
Решение ниже реализовано на примере нетипового справочника "ТарифыМобильнойСвязи" разработанного в конфигурации КА2, с целью учета корпоративной мобильной связи в коммерческом предприятии. . . .
Автозаполнение реквизита при выборе элемента справочника
Maks 27.03.2026
Программный код из решения ниже на примере нетипового документа "ЗаявкаНаРемонтСпецтехники" разработанного в конфигурации КА2. При выборе "Спецтехники" (Тип Справочник. Спецтехника), заполняется. . .
Сумматор с применением элементов трёх состояний.
Hrethgir 26.03.2026
Тут. https:/ / fips. ru/ EGD/ ab3c85c8-836d-4866-871b-c2f0c5d77fbc Первый документ красиво выглядит, но без схемы. Это конечно не даёт никаких плюсов автору, но тем не менее. . . всё может быть. . .
Автозаполнение реквизитов при создании документа
Maks 26.03.2026
Программный код из решения ниже размещается в модуле объекта документа, в процедуре "ПриСозданииНаСервере". Алгоритм проверки заполнения реализован для исключения перезаписи значения реквизита,. . .
Команды формы и диалоговое окно
Maks 26.03.2026
1. Команда формы "ЗаполнитьЗапчасти". Программный код из решения ниже на примере нетипового документа "ЗаявкаНаРемонтСпецтехники" разработанного в конфигурации КА2. В качестве источника данных. . .
Кому нужен AOT?
DevAlt 26.03.2026
Решил сделать простой ланчер Написал заготовку: dotnet new console --aot -o UrlHandler var items = args. Split(":"); var tag = items; var id = items; var executable = args;. . .
Отправка уведомления на почту при создании или изменении элементов справочника
Maks 24.03.2026
Программная отправка письма электронной почты на примере типового справочника "Склады" в конфигурации БП3. Перед реализацией необходимо выполнить настройку системной учетной записи электронной. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru