投影 IQueryable 会导致 SQL Server 抛出异常

问题描述 投票:0回答:1

我正在使用 Automapper 的可查询扩展的

ProjectTo
来查询基于 DTO 类可查询的 EF Core 6 实体。这些实体是相当大的对象图,但投影一直运行良好。除了一个实体。

根据过滤后的数据(因此这取决于返回的数据),EF Core 从 SQL Server 收到错误消息:

Microsoft.Data.SqlClient.SqlException (0x80131904):无法创建大小为 8310 的行,该行大小大于允许的最大行大小 8060。

需要注意的是:整个数据库是通过代码优先数据迁移创建的(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; }
    //... Other properties and collections
}

public class Baz
{
    public int Quantity { get; set; }
    //... Other properties and collections
}

// Projection
IQueryable<FooDTO> foosQuery = dbContext.Foos
    .AsNoTracking()
    .ProjectTo<FooDTO>(mapperConfiguration);

使用下面的投影配置将在执行时抛出异常:

var mapperConfiguration = new MapperConfiguration(cfg =>
{
    cfg.CreateProjection<Foo, FooDTO>();
    cfg.CreateProjection<Bar, BarDTO>();
    cfg.CreateProjection<Baz, BazDTO>();
});

此配置也失败:

var mapperConfiguration = new MapperConfiguration(cfg =>
{
    cfg.CreateProjection<Foo, FooDTO>();
    cfg.CreateProjection<Bar, BarDTO>()
        // Ignore all properties individually like this:
        .ForMember(x => x.Description, opt => opt.Ignore());
    cfg.CreateProjection<Baz, BazDTO>();
});

但这并没有失败:

var mapperConfiguration = new MapperConfiguration(cfg =>
{
    cfg.CreateProjection<Foo, FooDTO>()
        .ForMember(x => x.Bars, opt => opt.Ignore());
    cfg.CreateProjection<Bar, BarDTO>();
    cfg.CreateProjection<Baz, BazDTO>();
});

下面针对实体的查询也不会失败:

IQueryable<Foo> foosQuery = dbContext.Foos
    .Include(x => x.Bars).Include(x => x.Bazs).AsNoTracking();
c# entity-framework-core automapper projection
1个回答
0
投票

在(总是乐于助人且知识渊博!)Ivan Stoev 的指导下,通过使用

AsSplitQuery
拆分查询解决了该问题。

单一查询模式(默认)将创建包含从 Foo 中选择的所有列 + 从 Bar 中选择的所有列 + 从 Baz 中选择的所有列的查询。 EF Core 无法跟踪这一点,而且这是特定的 SqlServer 限制。拆分查询将执行 3 个 SQL 查询,仅包含相应表中选定的列。

// Projection
IQueryable<FooDTO> foosQuery = dbContext.Foos
    .AsNoTracking()
    .AsSplitQuery()
    .ProjectTo<FooDTO>(mapperConfiguration);
© www.soinside.com 2019 - 2024. All rights reserved.