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

AutoMapper映射时抛出LINQ无法构造复杂类型异常的排查

问题描述

我创建了如下AutoMapper配置:

CreateMap<Records_Log, Records_LogDto>()
                .ForMember(dest => dest.UserName, opt =>
                {
                    opt.ResolveUsing(d => d.User.UserName);
                });

对应实体类定义如下:

Records_Log 实体

public class Records_Log
{
    public int Id { get; set; }
    public string Table_name { get; set; }

    [ForeignKey("Records")]
    public int RecordsId { get; set; }
    public Records Records { get; set; }
    public  int RecordId { get; set;  }
    public string OldValue { get; set; }
    public string NewValue { get; set; }

    [ForeignKey("User")]
    public string UserId { get; set; }
    public ApplicationUser User { get; set; }

    public DateTime ChangeDate { get; set; }

    public string Action { get; set; }
}

Records_LogDto 实体

public class Records_LogDto
{
    public int Id { get; set; }
    public string Table_name { get; set; }

    public int RecordsId { get; set; }
    public Records Records { get; set; }

    public int RecordId { get; set; }
    public string OldValue { get; set; }
    public string NewValue { get; set; }

    public string UserId { get; set; }

    public string UserName { get; set; }

    public string Action { get; set; }

    public DateTime ChangeDate { get; set; }
}

Records 实体

public class Records
{
    public int Id { get; set; }

    public string SerialName { get; set; }

    public string RecordsName { get; set; }

    public string Url { get; set; }
    public string AttachmentPath { get; set; }
    public string ImagePath { get; set; }

    public string RecordsNameModified { get; set; }

    public bool isDeleted { get; set; }

    public bool isRead { get; set; }

    public bool IsHidden { get; set; }
    public bool MarkAsImportant { get; set; }
    public string ImportantComments { get; set; }

    public int subTypeID { get; set; }

    public virtual ICollection<Records_Rate> Records_Rate { get; set; }
}

编写的LINQ查询代码如下:

var query = (from st in db.Records_State
             join sl in db.Records_Log
             on st.Records.Id equals sl.RecordsId
             where st.Records.isDeleted
             && sl.Table_name == nameof(Records)
             && sl.Action == "Deleted"
             && sl.NewValue == "Top"
             && (string.IsNullOrEmpty(recordsName) || sl.Records.RecordsName.Contains(recordsName)
                || sl.Records.RecordsNameModified.Contains(recordsName))
             && (fromDate == null || sl.ChangeDate >= fromDate)
             && (toDate == null || sl.ChangeDate <= toDate)
             orderby sl.ChangeDate descending
             select new Records_Log { 
                 Records= st.Records, 
                 ChangeDate= sl.ChangeDate 
             })
             .AsQueryable();

query = from p in query
        group p by p.Records.Id
        into pGroup
        orderby pGroup.Key
        select pGroup.FirstOrDefault();

var result = query.Skip((pageNumber - 1) * pageSize)
          .Take(pageSize)
          .OrderByDescending(st => st.Id);

var dto = Mapper.Map<List<Records_LogDto>>(result);

执行最后一行var dto = Mapper.Map<List<Records_LogDto>>(result);时,抛出以下异常:

Error mapping types.
Mapping types:
DbQuery1 -> List1
System.Data.Entity.Infrastructure.DbQuery1[[PortalBackEndAPI.Models.Records_Log, PortalBackEndAPI.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] -> System.Collections.Generic.List1[[PortalBackEndAPI.Dto.Records_LogDto, PortalBackEndAPI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]"

以及:

The entity or complex type 'PortalBackEndAPI.Models.Records_Log' cannot be constructed in a LINQ to Entities query


异常原因
  1. LINQ to Entities 限制构造实体:EF不允许在LINQ查询中直接通过new Records_Log { ... }构造EF实体类实例,这种操作无法被转换为SQL语句,这是核心触发点。
  2. 延迟加载的映射冲突:result是未执行的DbQuery对象,AutoMapper尝试直接映射查询对象而非实际数据集合,导致类型不匹配错误。

解决方法

方法一:用匿名类承接查询结果,直接映射到DTO

避免在查询中构造实体类,改用匿名类获取所需字段,再转换为DTO:

var query = (from st in db.Records_State
             join sl in db.Records_Log
             on st.Records.Id equals sl.RecordsId
             where st.Records.isDeleted
             && sl.Table_name == nameof(Records)
             && sl.Action == "Deleted"
             && sl.NewValue == "Top"
             && (string.IsNullOrEmpty(recordsName) || sl.Records.RecordsName.Contains(recordsName)
                || sl.Records.RecordsNameModified.Contains(recordsName))
             && (fromDate == null || sl.ChangeDate >= fromDate)
             && (toDate == null || sl.ChangeDate <= toDate)
             orderby sl.ChangeDate descending
             // 用匿名类承接所有需要的字段
             select new { 
                 sl.Id,
                 sl.Table_name,
                 sl.RecordsId,
                 Records = st.Records,
                 sl.RecordId,
                 sl.OldValue,
                 sl.NewValue,
                 sl.UserId,
                 sl.Action,
                 sl.ChangeDate,
                 UserName = sl.User.UserName
             })
             .AsQueryable();

// 分组逻辑保持不变
query = from p in query
        group p by p.Records.Id
        into pGroup
        orderby pGroup.Key
        select pGroup.FirstOrDefault();

// 执行查询并加载到内存
var resultList = query.Skip((pageNumber - 1) * pageSize)
                      .Take(pageSize)
                      .OrderByDescending(st => st.Id)
                      .ToList();

// 直接构造DTO集合
var dto = resultList.Select(item => new Records_LogDto
{
    Id = item.Id,
    Table_name = item.Table_name,
    RecordsId = item.RecordsId,
    Records = item.Records,
    RecordId = item.RecordId,
    OldValue = item.OldValue,
    NewValue = item.NewValue,
    UserId = item.UserId,
    UserName = item.UserName,
    Action = item.Action,
    ChangeDate = item.ChangeDate
}).ToList();

方法二:先加载实体到内存再处理

如果需要保留实体类操作,先将查询结果加载到内存,再进行分组和映射:

// 查询完整数据并预加载导航属性,加载到内存
var rawData = (from st in db.Records_State
               join sl in db.Records_Log
               on st.Records.Id equals sl.RecordsId
               where st.Records.isDeleted
               && sl.Table_name == nameof(Records)
               && sl.Action == "Deleted"
               && sl.NewValue == "Top"
               && (string.IsNullOrEmpty(recordsName) || sl.Records.RecordsName.Contains(recordsName)
                  || sl.Records.RecordsNameModified.Contains(recordsName))
               && (fromDate == null || sl.ChangeDate >= fromDate)
               && (toDate == null || sl.ChangeDate <= toDate)
               orderby sl.ChangeDate descending
               select sl)
               .Include(l => l.Records) // 预加载Records
               .Include(l => l.User) // 预加载User,避免延迟加载问题
               .ToList(); // 执行查询,加载到内存

// 内存中分组分页
var groupedData = rawData.GroupBy(p => p.Records.Id)
                         .OrderBy(g => g.Key)
                         .Select(g => g.FirstOrDefault())
                         .Skip((pageNumber - 1) * pageSize)
                         .Take(pageSize)
                         .OrderByDescending(st => st.Id)
                         .ToList();

// 正常映射
var dto = Mapper.Map<List<Records_LogDto>>(groupedData);

额外优化建议

  • 避免在DTO中直接包含Records实体,建议创建RecordsDto并配置AutoMapper嵌套映射,降低实体耦合。
  • 始终用Include预加载所需导航属性,避免映射时触发延迟加载导致额外查询或报错。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 16:45:54