0

所以我在 ADO.NET 中运行一个我知道返回单行的查询。我的代码的相关部分如下所示:

SqlCommand sqc = new SqlCommand();
sqc.Connection = new SqlConnection("connection string");
sqc.CommandText = "SELECT A, B FROM C WHERE D=@d";
sqc.Parameters.AddWithValue("@d", "d");
sqc.Connection.Open();
SqlDataReader rdr = sqc.ExecuteReader();
bool boolio = rdr.Read();
String a = (String)rdr["A"];

当我运行它时,我在最后一行出现异常,抱怨我正在尝试读取没有数据的地方。

当我在 VS 中逐步执行此操作时,我会在 rdr.Read() 行执行之前检查 rdr,我可以看到我想要的数据位于阅读器中。然后我调用 rdr.Read() 结果为 FALSE,表示没有更多数据可供我读取。当我再次检查 rdr 时,内部 ResultsView 是空的。

我意识到我可以简单地通过放弃对 Read() 的调用来获取我的数据,但是:这种行为与MSDN 文档相反,该文档明确指出“SqlDataReader 的默认位置在第一条记录之前。因此,您必须调用 Read 开始访问任何数据。”。这也与我在网上找到的每个 SqlDataReader 用法示例相反,例如这个 SO question,它的存在似乎只是为了嘲讽我。

请注意,我已经针对 .NET 3.5 和 .NET 4.5 测试了我的代码,结果相同。

我错过了什么吗?ADO.NET 中是否存在错误?任何输入表示赞赏!

更新1:我在最后一行得到的实际异常是:

System.Data.dll 中出现“System.InvalidOperationException”类型的未处理异常

附加信息:不存在数据时尝试读取无效。

更新 2:

由于 Nathan Skerl 无法重现此行为,并且发布了经过验证的工作代码,但后来对我来说失败了,我怀疑我们运行的平台可能存在问题。作为记录,我使用的是 Windows 7 Professional(64 位)Service Pack 1,并针对 .NET Framework 3.5 和 4.5 编译了我的项目,结果相同。如果有任何我没有包含的相关信息,请告诉我,我会添加它。

4

2 回答 2

2

更新:经过一些故障排除后,我们意识到阅读器是空的,因为通过扩展结果视图(调用Read()并使用行)在调试器中检查阅读器。

使用您的代码,我在 Linqpad 中运行了这个单元测试,结果符合预期,初始 reader.read() 前进到行“ONE”,第二次调用行“TWO”。您可以通过以下测试重现您的错误:

SqlCommand sqc = new SqlCommand();
sqc.Connection = (SqlConnection)this.Connection;
sqc.CommandText = "SELECT 'ONE' [A] union all select 'TWO' order by [A] asc;";
sqc.Parameters.AddWithValue("@d", "d");
sqc.Connection.Open();
SqlDataReader rdr = sqc.ExecuteReader();

bool boolio = rdr.Read();
String a = (String)rdr["A"];
a.Dump();

rdr.Read();
String b = (String)rdr["A"];
b.Dump();

你能不能告诉我们这个模式返回了什么:

using (SqlConnection connection = (SqlConnection)this.Connection)
using (SqlCommand sqc = new SqlCommand("select 'ONE' as [A] union all select 'TWO';", connection))
{
   connection.Open();

   using (SqlDataReader rdr = sqc.ExecuteReader())
   {
       while (rdr.Read())
       {
          Console.WriteLine(String.Format("{0}", (String)rdr["A"]));
       }
   }
}
于 2013-05-21T22:50:25.107 回答
0

我在尝试读取从 SP 返回的 1 行时遇到了完全相同的问题,使用 Microsoft Dynamics 2013R2 中的 ADO SQLCommand 调用。

具体来说,我使用这个 C/AL 代码来调用我的 SP:

SQLCommand := SQLCommand.SqlCommand();
SQLCommand.CommandText := 'Custom_Item_Card_report_Get_Item_Qties'; //name of SP
SQLCommand.Connection := SQLConnection;
SQLCommand.CommandType := SQLCommand.CommandType.StoredProcedure;
SQLCommand.CommandTimeout := 0;

IF SQLCommand.Parameters.Count = 0 THEN BEGIN
SQLCommand.Parameters.AddWithValue('@CompanyName', COMPANYNAME);
SQLCommand.Parameters.AddWithValue('@ItemNo', InputItemCode);
SQLCommand.Parameters.AddWithValue('@FromDate', InputDateFrom);
SQLCommand.Parameters.AddWithValue('@Location', InputLocation);
END;

SQLReader := SQLCommand.ExecuteReader;
//IF SQLReader.Read() THEN BEGIN
rs1QtyCumulative := SQLReader.Item('QtyCumulative');
MESSAGE(FORMAT(rs1QtyCumulative));
//END;

现在,在我的结果集中,我只有 2 行(十进制 3 列)。第 0 行和第 1 行,我们称之为。

如本期所述,如果没有 IF 附件,它将给我消息,其中包含来自SP 的结果集的第1 行、第 1 列的数据。

使用IF 和 .Read() 会给出一个错误,指出在不存在的情况下发出了读取数据的命令。它指的是 MESSAGE 之前的代码行。

SQLReader.Item('QtyCumulative');

如本期所述,这与明确指出.ExecuteReader() 将使SQLReader集合在执行后指向数据开始之前的行的文档相矛盾。

这肯定不会发生,最重要的是,如果我们确实为 SQLReader 对象调用 .Read ()方法,那么在调用 .ExecuteReader() 之后,它将立即移动到结果集的第二行。

显然,它们与SqlDataReader对象有一些调试。

于 2014-07-10T14:27:38.943 回答