You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Automapper映射双实体到GetLoginResponse失败,报自映射缺失异常

AutoMapper映射失败问题:合并User与UsersProvider到GetLoginResponse的解决方案

问题背景

需要将User和UsersProvider两个实体映射到合并后的DTOGetLoginResponse,但调用时抛出映射异常,尝试多种配置后问题仍未解决。

相关代码定义

实体定义

public class User
{
    public int UId { get; set; }

    public string UName { get; set; } = null!;

    public string UUsername { get; set; } = null!;

    public string UPassword { get; set; } = null!;

    public string? UEmail { get; set; }

    public int ULevel { get; set; }

    public int? UAddBy { get; set; }

    public DateTime? UAddDate { get; set; }

    public int? ULastEditBy { get; set; }

    public DateTime? ULastEditDate { get; set; }

    public string? UActive { get; set; }
}

public class UsersProvider
{
    public int UpId { get; set; }

    public int UpUserId { get; set; }

    public int? UpProvId { get; set; }

    public int? UpAddBy { get; set; }

    public DateTime? UpAddDate { get; set; }
}

合并响应DTO定义

public class GetLoginResponse
{
    public string? Active { get; set; }
    public string Password { get; set; }
    public int UserID { get; set; }
    public int Level { get; set; }
    public string Name { get; set; }
    public int? RecruitmentCenterID { get; set; }
}

AutoMapper映射类

public class GetLoginResponseMapper: Profile
{
    public GetLoginResponseMapper()
    {
        //CreateMap<User, GetLoginResponse>(MemberList.Source);
        //CreateMap<UsersProvider, GetLoginResponse>(MemberList.Source)
        //      .ForMember(d => d.RecruitmentCenterID, a => a.MapFrom(s => s.UpProvId));

        //CreateMap<GetLoginResponse, (User, UsersProvider)>(MemberList.Destination)
        //       .ForMember(x => x.Item1, opts => opts.MapFrom(x => x))
        //       .ForMember(x => x.Item2, opts => opts.MapFrom(x => x.RecruitmentCenterID))
        //       .ReverseMap();

        CreateMap<(User user, UsersProvider usersProvider), GetLoginResponse>()
                .ForMember(d => d.Active, opt => opt.MapFrom(s => s.user.UActive))
                .ForMember(d => d.Password, opt => opt.MapFrom(s => s.user.UPassword))
                .ForMember(d => d.UserID, opt => opt.MapFrom(s => s.user.UId))
                .ForMember(d => d.Level, opt => opt.MapFrom(s => s.user.ULevel))
                .ForMember(d => d.Name, opt => opt.MapFrom(s => s.user.UName))
                .ForMember(d => d.RecruitmentCenterID, a => a.MapFrom(s => s.usersProvider.UpProvId));

        //CreateMap<User, GetLoginResponse>();
    }
}

查询处理Handler

public class GetLoginQueryHandler : IRequestHandler<GetLoginRequest, List<GetLoginResponse>>
{
    private readonly ICrewApplicationDBContext _iCrewApplicationDBContext;

    private readonly IMapper _mapper;

    public GetLoginQueryHandler(ICrewApplicationDBContext iCrewApplicationDBContext, IMapper mapper)
    {
        _iCrewApplicationDBContext = iCrewApplicationDBContext;
        _mapper = mapper;
    }

    public Task<List<GetLoginResponse>> Handle(GetLoginRequest request, CancellationToken cancellationToken)
    {
        //return _iCrewApplicationDBContext.user.ProjectTo<GetLoginResponse>(_mapper.ConfigurationProvider).Where(a => a.UUsername == request.UserName).ToListAsync();

        return (from u in _iCrewApplicationDBContext.user
                join up in _iCrewApplicationDBContext.usersProvider on u.UId equals up.UpUserId
                into loginResult
                from lr in loginResult.DefaultIfEmpty()
                where u.UName == request.UserName
                select new GetLoginResponse
                {
                    Active = u.UActive,
                    Password = u.UPassword,
                    UserID = u.UId,
                    Level = u.ULevel,
                    Name = u.UName,
                    RecruitmentCenterID = lr.UpProvId 
                })   
                .ProjectTo<GetLoginResponse>(_mapper.ConfigurationProvider)
                .ToListAsync();
    }
}

异常信息

System.InvalidOperationException: Missing map from CrewApplication.Architecture.Application.AdminLogin.GetLoginResponse to CrewApplication.Architecture.Application.AdminLogin.GetLoginResponse. Create using CreateMap<GetLoginResponse, GetLoginResponse>.
at AutoMapper.QueryableExtensions.Impl.ProjectionBuilder.CreateProjection(ProjectionRequest& request, LetPropertyMaps letPropertyMaps)
...(省略后续堆栈信息)

解决方法

方案1:移除多余的ProjectTo调用

当前代码中已经通过LINQ查询手动构造了GetLoginResponse对象,但后续又调用了ProjectTo<GetLoginResponse>,AutoMapper会尝试将GetLoginResponse作为源类型映射到目标类型GetLoginResponse,但未配置该映射,因此抛出异常。

直接移除ProjectTo调用即可解决:

public Task<List<GetLoginResponse>> Handle(GetLoginRequest request, CancellationToken cancellationToken)
{
    return (from u in _iCrewApplicationDBContext.user
            join up in _iCrewApplicationDBContext.usersProvider on u.UId equals up.UpUserId
            into loginResult
            from lr in loginResult.DefaultIfEmpty()
            where u.UName == request.UserName
            select new GetLoginResponse
            {
                Active = u.UActive,
                Password = u.UPassword,
                UserID = u.UId,
                Level = u.ULevel,
                Name = u.UName,
                RecruitmentCenterID = lr?.UpProvId // 添加空引用运算符,避免lr为null时出错
            })   
            .ToListAsync();
}

方案2:使用AutoMapper完成投影(推荐)

如果希望通过AutoMapper统一管理映射规则,可修改查询逻辑,直接从联合查询的结果投影到GetLoginResponse,无需手动构造DTO:

  1. 调整映射配置:
public class GetLoginResponseMapper: Profile
{
    public GetLoginResponseMapper()
    {
        CreateMap<(User user, UsersProvider usersProvider), GetLoginResponse>()
                .ForMember(d => d.Active, opt => opt.MapFrom(s => s.user.UActive))
                .ForMember(d => d.Password, opt => opt.MapFrom(s => s.user.UPassword))
                .ForMember(d => d.UserID, opt => opt.MapFrom(s => s.user.UId))
                .ForMember(d => d.Level, opt => opt.MapFrom(s => s.user.ULevel))
                .ForMember(d => d.Name, opt => opt.MapFrom(s => s.user.UName))
                .ForMember(d => d.RecruitmentCenterID, opt => opt.MapFrom(s => s.usersProvider?.UpProvId)); // 添加空引用处理
    }
}
  1. 修改Handler中的查询逻辑:
public Task<List<GetLoginResponse>> Handle(GetLoginRequest request, CancellationToken cancellationToken)
{
    return _iCrewApplicationDBContext.user
        .GroupJoin(_iCrewApplicationDBContext.usersProvider, 
            u => u.UId, 
            up => up.UpUserId, 
            (user, providers) => new { user, providers })
        .SelectMany(x => x.providers.DefaultIfEmpty(), 
            (x, provider) => (user: x.user, usersProvider: provider))
        .Where(x => x.user.UName == request.UserName)
        .ProjectTo<GetLoginResponse>(_mapper.ConfigurationProvider)
        .ToListAsync();
}

这样AutoMapper会根据配置的元组映射规则,直接将查询结果投影到GetLoginResponse,避免手动构造DTO的重复代码。

内容的提问来源于stack exchange,提问作者vivek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 03:52:02