Чтобы выбрать из ссылок на вложенные дочерние таблицы, я использую подход, заключающийся в том, чтобы выбрать анонимный тип для построения эффективного запроса, а затем сократить его до DTO.
Например:
var users = userManager.Users.Select(x => new
{
UserName = x.UserName,
Email = x.Email,
Family = x.Family,
Name = x.Name,
Password = x.PasswordHash,
PhoneNumber = x.PhoneNumber,
Role = x.UserRoles.Select(r => new { r.RoleId, r.Role.RoleName}).FirstOrDefault()
}).ToList()
.Select(x => new UserDto
{
UserName = x.UserName,
Email = x.Email,
Family = x.Family,
Name = x.Name,
Password = x.PasswordHash,
PhoneNumber = x.PhoneNumber,
RoleId = x.Role?.RoleId,
RoleName = x.Role?.RoleName
}).ToList();
Вы можете обойтись одним .Select
, хотя это может привести к менее эффективному SQL, так как каждый внутренний .FirstOrDefault()
и т. Д., Вероятно, повторно присоединит таблицы UserRole / Role. Таким образом, первый .Select()
строит SQL для вложенной структуры, содержащей дочерние данные, которые нам нужны, затем второй работает с результирующими анонимными типами POCO, создавая плоский DTO для возврата.
И последнее: при использовании .First()
/ .FirstOrDefault()
вы должны использовать предложение .OrderBy()
, чтобы обеспечить предсказуемый результат, если вы хотите получить только одну запись из возможных многих.
Кроме того, учитывая, что мы используем .Select()
для отображения нашего вывода, вам не нужно использовать .Include()
для выбора из связанных объектов. Вам также не нужно выражение .Where()
внутри ваших User.UserRoles. (User.UserRoles будет содержать только UserRoles для этого UserId). Здесь вы увидите, что .Where()
предложения используются при соединении свободно связанных таблиц из DbContext. Непосредственно связанные отношения сущностей через сопоставленные FK предпочтительнее.
Редактировать: учитывая, что у каждого пользователя может не быть роли, во втором .Select()
вам нужно будет использовать «?». оператор для получения идентификатора роли и имени. Это будет означать, что идентификатор роли DTO должен быть нулевым. Если вы хотите использовать RoleId по умолчанию 0 или около того, вместо этого вы можете использовать RoleId = x.Role?.RoleId ?? 0