SqlDataReader无数据但SQL Profiler显示存储过程返回行的问题
我正在开发一个使用C#实现搜索功能的应用,代码通过SqlCommand调用[dbo].[usp_Search]存储过程,设置了约20个参数。触发搜索时,SQL Profiler中显示的执行语句如下:
exec [dbo].[usp_Search] @xmlPolicyId=NULL,@xmlLastName=NULL,@xmlFirstName=NULL,@xmlCompanyName=NULL,@xmlAddress=N'%TCHOUPITOULAS%',@xmlAddress2=NULL,@xmlCity=NULL,@xmlState=NULL,@xmlZipCode=NULL,@xmlCountry=NULL,@xmlQuotesOnly=0,@xmlHistory=0,@xmlEffectiveDate='1753-01-01 00:00:00',@xmlAgencyName=NULL,@xmlAgentId=NULL,@xmlssn=NULL,@xmlfein=NULL,@xmlPolicyStatus=NULL,@xmlPolicyType=NULL,@xmlUserLevel=N'5'
该语句与预期一致,在SSMS中执行同一语句可返回约300行数据。但通过SqlDataReader读取时:
commercialConn.Open(); using (SqlDataReader dr = cmd.ExecuteReader()) { while (dr.Read()) //dr._hasRows = false { // 从未进入此代码块 } }
确认连接正确、参数无误,Profiler显示语句已执行且有返回行,但SqlDataReader却无法读取到数据,寻求原因及解决方法。
存储过程返回多个结果集
存储过程中如果存在PRINT语句、未添加SET NOCOUNT ON,或者先返回了其他计算结果(比如影响行数),会导致SqlDataReader先读取到空的结果集,真正的数据在后续结果集中。解决方法:- 在存储过程开头添加
SET NOCOUNT ON;,避免返回额外的影响行数结果; - 在代码中调用
dr.NextResult()跳过前面的空结果集,再执行dr.Read()读取数据。
- 在存储过程开头添加
参数类型/精度不匹配
即使Profiler显示参数值正确,C#中设置SqlParameter时如果未指定正确的SqlDbType,ADO.NET自动推断类型可能导致隐性转换,和SSMS执行时的参数类型不一致。比如@xmlEffectiveDate如果是datetime类型,在C#中要显式设置parameter.SqlDbType = SqlDbType.DateTime,而不是依赖默认推断。执行上下文/权限差异
检查C#应用使用的数据库登录账号和SSMS登录账号是否一致:- 如果存在行级安全(RLS),应用账号可能没有权限读取对应数据;
- 两个账号的默认数据库、隔离级别或会话设置不同,导致查询结果不一致。可以在Profiler中确认执行语句的登录账号,和SSMS使用的账号对比。
存储过程内部条件分支逻辑
存储过程可能存在依赖会话上下文(如SESSION_CONTEXT)、调用方标识的分支逻辑,即使参数相同,不同调用环境下执行路径不同。可以在存储过程中添加日志输出(如写入临时表),跟踪实际执行的分支,或者在Profiler中捕获存储过程内部的执行语句。未处理的DataReader状态异常
虽然代码没有抛出显式异常,但可能存在隐性错误导致DataReader无法正确读取。可以在代码中添加判断:using (SqlDataReader dr = cmd.ExecuteReader()) { if (dr.HasRows) { while (dr.Read()) { // 读取逻辑 } } else { // 检查dr的状态,或输出cmd的参数信息排查 } }同时可以捕获
SqlException,查看是否有未被注意的警告或错误。
内容的提问来源于stack exchange,提问作者awimley

