EFCore join table and AutoMapper
EFCore join table and AutoMapper
Question
I want to query all users from my ASP.net Identity Users table and map them to a simple DTO like this:
public class UserDto
{
public string Firstname { get; set; }
public string Lastname { get; set; }
public string Email { get; set; }
public IEnumerable<string> Roles { get; set; }
}
The list of roles should only contain the names of the roles, so I join the roles in from the roles table and get the names. Now I want to simplify this by using AutoMapper and map the results directly into my DTO.
var users = await _userManager.Users
.AsNoTracking()
.Include(u => u.Roles)
.Select(u => new {
User = u,
Roles = u.Roles
.Join(_roleManager.Roles,
a => a.RoleId,
b => b.Id,
(a, b) => b.Name)
.ToList()
})
.ToListAsync();
I'm struggling to find a good solution to map this data to a list of UserDto objects with AutoMapper. I tried to user ProjectTo<UserDto>
and implement the table join in my mapper configuration but I get a lot of efcore warnings that my queries are executed on the client.
Question: Is there a simple and efficient way to do this with AutoMapper and efcore?
Update Even without AutoMapper it produces a warning :(
var users = await _userManager.Users
.AsNoTracking()
.Include(u => u.Roles)
.Select(u => new UserDto {
Firstname = u.Firstname,
Lastname = u.Lastname,
Email = u.Email,
Roles = u.Roles
.Join(_roleManager.Roles,
a => a.RoleId,
b => b.Id,
(a, b) => b.Name)
.ToList()
})
.ToListAsync();
This is the efcore logging output:
info: Microsoft.EntityFrameworkCore.Storage.IRelationalCommandBuilderFactory[1]
Executed DbCommand (2ms) [Parameters=[@__get_Item_0='?' (Size = 450)], CommandType='Text', CommandTimeout='30']
SELECT TOP(1) [e].[Id], [e].[AccessFailedCount], [e].[Address], [e].[City], [e].[ConcurrencyStamp], [e].[Country], [e].[CustomerIdentifier], [e].[Email], [e].[EmailConfirmed], [e].[Firstname], [e].[Gender], [e].[Lastname], [e].[LockoutEnabled], [e].[LockoutEnd], [e].[NormalizedEmail], [e].[NormalizedUserName], [e].[PasswordHash], [e].[PhoneNumber], [e].[PhoneNumberConfirmed], [e].[Region], [e].[SecurityStamp], [e].[TwoFactorEnabled], [e].[UserName], [e].[ZipCode]
FROM [AspNetUsers] AS [e]
WHERE [e].[Id] = @__get_Item_0
info: Microsoft.EntityFrameworkCore.Storage.IRelationalCommandBuilderFactory[1]
Executed DbCommand (2ms) [Parameters=[@__normalizedRoleName_0='?' (Size = 256)], CommandType='Text', CommandTimeout='30']
SELECT TOP(2) [r].[Id], [r].[ConcurrencyStamp], [r].[Name], [r].[NormalizedName]
FROM [AspNetRoles] AS [r]
WHERE [r].[NormalizedName] = @__normalizedRoleName_0
info: Microsoft.EntityFrameworkCore.Storage.IRelationalCommandBuilderFactory[1]
Executed DbCommand (6ms) [Parameters=[@__get_Item_0='?' (Size = 450), @__get_Item_1='?' (Size = 450)], CommandType='Text', CommandTimeout='30']
SELECT TOP(1) [e].[UserId], [e].[RoleId]
FROM [AspNetUserRoles] AS [e]
WHERE ([e].[UserId] = @__get_Item_0) AND ([e].[RoleId] = @__get_Item_1)
warn: Microsoft.EntityFrameworkCore.Query.Internal.SqlServerQueryCompilationContextFactory[6]
The Include operation for navigation: 'u.Roles' was ignored because the target navigation is not reachable in the final query results. To configure this warning use the DbContextOptionsBuilder.ConfigureWarnings API (event id 'CoreEventId.IncludeIgnoredWarning'). ConfigureWarnings can be used when overriding the DbContext.OnConfiguring method or using AddDbContext on the application service provider.
info: Microsoft.EntityFrameworkCore.Storage.IRelationalCommandBuilderFactory[1]
Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT [u].[Firstname], [u].[Lastname], [u].[Email], [u].[Id]
FROM [AspNetUsers] AS [u]
info: Microsoft.EntityFrameworkCore.Storage.IRelationalCommandBuilderFactory[1]
Executed DbCommand (2ms) [Parameters=[@_outer_Id='?' (Size = 450)], CommandType='Text', CommandTimeout='30']
SELECT [b].[Name]
FROM [AspNetUserRoles] AS [a]
INNER JOIN [AspNetRoles] AS [b] ON [a].[RoleId] = [b].[Id]
WHERE @_outer_Id = [a].[UserId]
info: Microsoft.EntityFrameworkCore.Storage.IRelationalCommandBuilderFactory[1]
Executed DbCommand (1ms) [Parameters=[@_outer_Id='?' (Size = 450)], CommandType='Text', CommandTimeout='30']
SELECT [b].[Name]
FROM [AspNetUserRoles] AS [a]
INNER JOIN [AspNetRoles] AS [b] ON [a].[RoleId] = [b].[Id]
WHERE @_outer_Id = [a].[UserId]
Update 2
Following the warning, I just removed the Include statement an found a working solution:
var users = await _userManager.Users
.AsNoTracking()
.Select(u => new UserDto {
Firstname = u.Firstname,
Lastname = u.Lastname,
Email = u.Email,
Roles = u.Roles
.Join(_roleManager.Roles,
a => a.RoleId,
b => b.Id,
(a, b) => b.Name)
.ToList()
})
.ToListAsync();
Accepted Answer
I removed the Include statement from the query, moved the select statement to my AutoMapper profile and added the roles table as a parameter to my ProjectTo statement. Now its working as expected and not producing any efcore warnings.
ef query
var users = await _userManager.Users
.AsNoTracking()
.ProjectTo<UserDto>(new { roles = _roleManager.Roles })
.ToListAsync();
automapper profile
IQueryable<IdentityRole> roles = null;
CreateMap<User, UserDto>()
.ForMember(x => x.Roles, opt =>
opt.MapFrom(src =>
src.Roles
.Join(roles, a => a.RoleId, b => b.Id, (a, b) => b.Name)
.ToList()
)
);
作者:Chuck Lu GitHub |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
2019-08-28 ajax调用c#后端,发现参数没数值
2017-08-28 sql server management studio 快速折叠object explorer中的instance
2017-08-28 DBCC-->Database Console Commands
2017-08-28 What is the difference between SET and SELECT when assigning values to variables, in T-SQL?
2015-08-28 TeeChart的最小步长和最大步长