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

使用AutoMapper ProjectTo投影EF Core实体触发SQL行大小超限异常

EF Core 6 + AutoMapper ProjectTo 触发SQL Server行大小超限问题

问题描述

我使用AutoMapper的ProjectTo扩展方法,基于DTO类的IQueryable查询EF Core 6实体。实体是较大的对象图,多数投影场景运行正常,但查询Foo实体时,当过滤数据满足特定条件,EF Core会收到SQL Server的错误提示:

Microsoft.Data.SqlClient.SqlException (0x80131904): Cannot create a row of size 8310 which is greater than the allowable maximum row size of 8060.

数据库通过Code First从EF Core 3.1迁移创建,已执行ALTER TABLE [dbo].[Foos] REBUILD重建所有表,但问题依旧。直接查询实体(不通过DTO投影)时无此异常。

相关代码结构

public class Foo
{
    public List<Bar> Bars { get; set; }
    public List<Baz> Bazs { get; set; }
}

public class Bar
{
    public string Description { get; set; }
    //... 其他属性和集合
}

public class Baz
{
    public int Quantity { get; set; }
    //... 其他属性和集合
}

// 投影查询代码
IQueryable<FooDTO> foosQuery = dbContext.Foos
    .AsNoTracking()
    .ProjectTo<FooDTO>(mapperConfiguration);

不同投影配置的测试结果

  1. 包含Foo、Bar、Baz的完整投影配置会触发异常:
var mapperConfiguration = new MapperConfiguration(cfg =>
{
    cfg.CreateProjection<Foo, FooDTO>();
    cfg.CreateProjection<Bar, BarDTO>();
    cfg.CreateProjection<Baz, BazDTO>();
});
  1. 单独忽略Bar的所有属性,仍会触发异常:
var mapperConfiguration = new MapperConfiguration(cfg =>
{
    cfg.CreateProjection<Foo, FooDTO>();
    cfg.CreateProjection<Bar, BarDTO>()
        // 逐个忽略所有属性,示例如下:
        .ForMember(x => x.Description, opt => opt.Ignore());
    cfg.CreateProjection<Baz, BazDTO>();
});
  1. 忽略Foo的Bars集合,不会触发异常:
var mapperConfiguration = new MapperConfiguration(cfg =>
{
    cfg.CreateProjection<Foo, FooDTO>()
        .ForMember(x => x.Bars, opt => opt.Ignore());
    cfg.CreateProjection<Bar, BarDTO>();
    cfg.CreateProjection<Baz, BazDTO>();
});
  1. 直接用Include关联集合查询实体,不会触发异常:
IQueryable<Foo> foosQuery = dbContext.Foos
    .Include(x => x.Bars).Include(x => x.Bazs).AsNoTracking();

问题根源与解决方法

核心原因

AutoMapper的ProjectTo处理集合导航属性时,会生成JOIN关联表的SQL语句,把关联表的字段拼接到主查询的行中。即使你忽略了Bar的所有属性,只要没有明确忽略Foo的Bars集合,AutoMapper仍会保留JOIN逻辑——SQL Server会将关联表的所有列(哪怕未被SELECT)计入行总大小,当Bar表包含大尺寸字段(如长字符串、大二进制数据)时,就会触发行大小超限错误。

而EF Core的Include默认采用拆分查询(EF Core 3.1+默认开启),会将关联集合的查询拆分为多个独立SQL,不会把关联表字段合并到主行中,因此不会触发该错误。

解决方案

  1. 明确忽略Foo的Bars集合:采用测试中的第3种配置,直接在Foo到FooDTO的投影中忽略Bars导航属性,避免生成JOIN Bar的SQL;
  2. 仅投影必要字段:若需要映射Bars集合,确保在Bar的投影配置中只选择实际需要的字段,避免引入大尺寸字段;
  3. 启用EF Core拆分查询:在查询中添加AsSplitQuery(),强制EF Core将关联集合的查询拆分为多个SQL,避免行大小超限:
    IQueryable<FooDTO> foosQuery = dbContext.Foos
        .AsSplitQuery()
        .AsNoTracking()
        .ProjectTo<FooDTO>(mapperConfiguration);
    

内容的提问来源于stack exchange,提问作者Superman.Lopez

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 22:05:38