我正在使用 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();
在(总是乐于助人且知识渊博!)Ivan Stoev 的指导下,通过使用
AsSplitQuery
拆分查询解决了该问题。
单一查询模式(默认)将创建包含从 Foo 中选择的所有列 + 从 Bar 中选择的所有列 + 从 Baz 中选择的所有列的查询。 EF Core 无法跟踪这一点,而且这是特定的 SqlServer 限制。拆分查询将执行 3 个 SQL 查询,仅包含相应表中选定的列。
// Projection
IQueryable<FooDTO> foosQuery = dbContext.Foos
.AsNoTracking()
.AsSplitQuery()
.ProjectTo<FooDTO>(mapperConfiguration);