EF Core执行存储过程返回-1,如何转Linq或获取正确结果?
问题解决:EF Core调用存储过程返回-1,及等价Linq实现
一、正确调用存储过程获取结果
你用ExecuteSqlRawAsync返回-1是因为这个方法的作用是返回SQL语句影响的行数,而你的存储过程是执行查询返回结果集,所以得用FromSqlRawAsync来获取查询结果。同时要使用参数化查询,避免SQL注入风险。
实现步骤:
- 先创建一个类用来接收存储过程的返回结果:
public class TotalRecordsResult { public int TotalRecords { get; set; } }
- 调用存储过程的正确代码:
// 定义参数,避免SQL注入 var userIdParam = new SqlParameter("@UserId", userId); var startDateParam = new SqlParameter("@StartDate", request.StartDate); var endDateParam = new SqlParameter("@EndDate", request.EndDate); // 执行存储过程并获取结果 var result = await _context.Set<TotalRecordsResult>() .FromSqlRaw("EXEC LeadScoring_CountClosedActions @UserId, @StartDate, @EndDate", userIdParam, startDateParam, endDateParam) .FirstOrDefaultAsync(); var count = result?.TotalRecords ?? 0;
这样就能正确拿到存储过程返回的统计值,而不是-1。
二、等价Linq实现(匹配原存储过程逻辑)
先拆解原存储过程的核心逻辑:
- 关联
LeadScoringActions和LeadScoringLeads表 - 按条件过滤记录(指定用户、日期范围、排除系统关闭类型、Lead.Extra不为空)
- 统计满足条件的记录中,不重复的Lead.Extra的数量(原存储过程用
GROUP BY+窗口函数的写法,本质就是统计去重后的Lead.Extra个数)
简洁高效的Linq写法:
var count = await _context.Actions .Where(a => a.AssignedToUserId == userId && a.ActionResultType != LeadScoringConstants.SystemClosed && a.DateCompleted > DateTime.Today.AddDays(request.StartDate) && a.DateCompleted <= DateTime.Today.AddDays(request.EndDate) && a.Lead.Extra != null) // 提取符合条件的Lead.Extra,去重后统计数量 .Select(a => a.Lead.Extra) .Distinct() .CountAsync();
如果要严格对应原存储过程的GROUP BY+窗口函数逻辑(虽然没必要,因为和上面写法等价),也可以这么写:
var groupedResult = await _context.Actions .Include(a => a.Lead) .Where(a => a.AssignedToUserId == userId && a.ActionResultType != LeadScoringConstants.SystemClosed && a.DateCompleted > DateTime.Today.AddDays(request.StartDate) && a.DateCompleted <= DateTime.Today.AddDays(request.EndDate) && a.Lead.Extra != null) .GroupBy(a => a.Lead.Extra) .Select(g => new { TotalGroups = _context.Actions .Where(a2 => a2.AssignedToUserId == userId && a2.ActionResultType != LeadScoringConstants.SystemClosed && a2.DateCompleted > DateTime.Today.AddDays(request.StartDate) && a2.DateCompleted <= DateTime.Today.AddDays(request.EndDate) && a2.Lead.Extra != null) .Select(a2 => a2.Lead.Extra) .Distinct() .Count() }) .FirstOrDefaultAsync(); var count = groupedResult?.TotalGroups ?? 0;
三、存储过程 vs Linq 的选择建议
- 若业务逻辑复杂、需要利用SQL特定优化(比如复杂索引、自定义函数),或者需要统一维护SQL逻辑,选存储过程更合适。
- 若追求代码可读性、强类型检查,且逻辑不复杂,Linq更优——它能自动处理实体映射,避免手动创建结果类,EF Core也会生成优化后的SQL。
- 无论选哪种方式,绝对不要用字符串拼接参数,必须用参数化查询防止SQL注入。
内容的提问来源于stack exchange,提问作者Pete
相关产品推荐
相关产品推荐

