EF Core使用ExecuteSqlInterpolatedAsync调用存储过程无法获取返回列表
解决EF Core中调用存储过程返回-1而非结果集的问题
问题原因
ExecuteSqlInterpolatedAsync 是EF Core专门用于执行非查询SQL操作(如INSERT、UPDATE、DELETE)的方法,它的返回值是受影响的行数。对于包含SELECT语句的存储过程,该方法无法解析并返回结果集;当EF Core无法统计到明确的受影响行数时,就会返回-1,这就是你遇到的情况。
另外代码里的Task.Run(() => _dbContext.Database.ExecuteSqlInterpolatedAsync(...))完全多余,因为ExecuteSqlInterpolatedAsync本身就是异步方法,直接await即可,不需要额外开启线程。
解决方案
根据你的返回结果是否有对应实体类,分两种处理方式:
方式一:结果集对应已有实体类
如果存储过程返回的字段和你的DbContext中某个实体类的属性完全匹配,可以使用FromSqlInterpolated来获取结果:
- 确保你的实体类(比如
ReportResult)结构与存储过程返回列匹配:
public class ReportResult { // 属性名需与存储过程返回的列名一致 public DateTime Date { get; set; } public string DataContent { get; set; } // 其他对应字段... }
- 修改方法代码:
public async Task<List<ReportResult>> Report(string sStartDate, string sEndDate, string spName) { try { var paramToDt = new SqlParameter("@TODATE", SqlDbType.VarChar) { Value = sStartDate }; var paramFrmDt = new SqlParameter("@FROMDATE", SqlDbType.VarChar) { Value = sEndDate }; // 直接调用FromSqlInterpolated获取结果集 var results = await _dbContext.Set<ReportResult>() .FromSqlInterpolated($"EXEC {spName} @TODATE={paramToDt}, @FROMDATE={paramFrmDt}") .ToListAsync(); return results; } catch (Exception ex) { throw ex; } }
方式二:自定义返回DTO(无对应实体类)
如果存储过程返回的结果没有对应的实体类,可以先定义一个DTO类,再使用SqlQuery<T>(EF Core 6.0+支持)来获取结果:
- 定义匹配存储过程返回列的DTO:
public class ReportDto { // 属性名需与存储过程返回的列名严格匹配(大小写取决于数据库配置) public int RecordId { get; set; } public decimal TotalAmount { get; set; } // 其他对应字段... }
- 修改方法代码:
public async Task<List<ReportDto>> Report(string sStartDate, string sEndDate, string spName) { try { var paramToDt = new SqlParameter("@TODATE", SqlDbType.VarChar) { Value = sStartDate }; var paramFrmDt = new SqlParameter("@FROMDATE", SqlDbType.VarChar) { Value = sEndDate }; // 使用SqlQuery执行存储过程并映射到DTO var results = await _dbContext.Database .SqlQuery<ReportDto>($"EXEC {spName} @TODATE=@p0, @FROMDATE=@p1", paramToDt, paramFrmDt) .ToListAsync(); return results; } catch (Exception ex) { throw ex; } }
额外注意事项
- 确保存储过程的参数名与代码中定义的参数名完全一致,避免参数传递错误。
- 如果存储过程返回多个结果集,需要调用
NextResultAsync()来遍历后续结果集。 - 移除多余的
Task.Run()包裹,直接await异步方法即可,减少不必要的线程开销。
内容的提问来源于stack exchange,提问作者Shibu
相关产品推荐
相关产品推荐

