Entity Framework:单事务中无关联多表多查询无需指定实体用Select
将指定SQL转换为Entity Framework无实体模型的查询实现
需求背景
需要将以下SQL查询转换为Entity Framework代码:
select (select sum(paid) from table_a) Paid, (select count(*) from table_b) ct
尝试过如下写法,但因Entity Framework要求Select()必须基于实体模型而无法运行:
await ctx.Select(u => new { Paid = ctx.table_a.Sum(i => i.paid), Ct = ctx.table_b.Count(i => i.key) }).ToArrayAsync();
已排除两个方案:
- 单独调用各查询(希望保持单事务)
- 使用
ExecuteSqlRawAsync或ExecuteSqlInterpolatedAsync(希望避免原生SQL)
解决方案
你可以通过构造虚拟单元素数据源来启动Select查询,既不依赖特定业务实体,又能在同一个数据库请求(事务)中完成多表聚合。
方法1:利用任意实体的Take(1)构造数据源
var result = await ctx.Set<object>() .Take(1) // 仅构造单元素数据源,不会实际查询数据 .Select(_ => new { Paid = ctx.table_a.Sum(i => i.paid), Ct = ctx.table_b.Count(i => i.key) }) .FirstOrDefaultAsync();
方法2:用FromSqlRaw构造常量数据源
var result = await ctx.FromSqlRaw("SELECT 1 AS Dummy") .Select(_ => new { Paid = ctx.table_a.Sum(i => i.paid), Ct = ctx.table_b.Count(i => i.key) }) .FirstOrDefaultAsync();
这两种写法最终生成的SQL与目标语句几乎一致,且整个操作会在同一事务中执行(若上下文已配置事务,或手动包裹using var transaction = await ctx.Database.BeginTransactionAsync())。
备选方案(EF Core 7+):用SqlQueryAsync映射匿名类型
若你的EF版本为Core 7及以上,可直接将原生SQL映射到匿名类型,相比ExecuteSqlRaw更简洁:
var result = await ctx.Database.SqlQueryAsync(new { Paid = default(decimal?), // 匹配sum(paid)的实际数据类型 Ct = default(int) }, "select (select sum(paid) from table_a) Paid, (select count(*) from table_b) ct") .FirstOrDefaultAsync();
内容的提问来源于stack exchange,提问作者jong shin
相关产品推荐
相关产品推荐

