将SqlDataReader写入立即窗口(C#)

6

我正在尝试调试一个SQL响应,但是出现了以下错误:

将varchar值“0.01”转换为位数据类型时转换失败。

这并不太合理,因为对象没有任何布尔值。

代码:

 using (var connection = _connectionProvider.GetDbConnection())
 {
    connection.Open();
    return connection.Query<Rate>(query, parameters);
 }

要执行的 SQL(我手动添加了参数):

select * from (select top 1 BuildNumber, RateVersion, SampleId, Tariff, TariffStepName, Factor1, Result1 from dbo.Rates
where Tariff = 'Default' and TariffStepName = 'I_P' and (RateVersion <= 1) and Factor1 = 'false' and (SampleId is null)
order by RateVersion desc, sampleId desc) top1 

我在发生读取操作的地方(connection.Query<Rate>(query, parameters))设置了断点,然后启用了异常断点,当它失败时跳转到更深层次的堆栈中的TdsParser TryRun()(抛出异常的几个级别更高)

System.Data.dll!System.Data.SqlClient.TdsParser.TryRun(System.Data.SqlClient.RunBehavior runBehavior,System.Data.SqlClient.SqlCommand cmdHandler,System.Data.SqlClient.SqlDataReader dataStream,System.Data.SqlClient.BulkCopySimpleResultSet bulkCopyHandler,System.Data.SqlClient.TdsParserStateObject stateObj,out bool dataReady) + 0x1ce1 bytes

此时,我可以访问SqlDataReader,即dataStream

我正在寻找一种直接从SqlDataReader输出“原始”结果的方法,例如:

System.Diagnostics.Debug.WriteLine((new System.IO.StreamReader(stream)).ReadToEnd());

但对于 SqlDataReader 来说。

编辑

根据评论中的请求。

public class Rate
{
    public string Tariff { get; set; }
    public string TariffStepName { get; set; }
    public string Factor1 { get; set; }
    public string Factor2 { get; set; }
    public string Factor3 { get; set; }
    public string Factor4 { get; set; }
    public string Factor5 { get; set; }
    public string Factor6 { get; set; }
    public string Factor7 { get; set; }
    public string Factor8 { get; set; }
    public string Factor9 { get; set; }
    public string Factor10 { get; set; }
    public decimal Result1 { get; set; }
    public decimal Result2 { get; set; }
    public decimal Result3 { get; set; }
    public decimal Result4 { get; set; }
    public decimal Result5 { get; set; }
    public decimal Result6 { get; set; }
    public decimal Result7 { get; set; }
    public decimal Result8 { get; set; }
    public decimal Result9 { get; set; }
    public decimal Result10 { get; set; }
    public string TextResult1 { get; set; }
    public string TextResult2 { get; set; }
    public string TextResult3 { get; set; }
    public string TextResult4 { get; set; }
    public string TextResult5 { get; set; }
    public int? SampleId { get; set; }
    public int BuildNumber { get; set; }
    public decimal? RateVersion { get; set; }
}

SQL

CREATE TABLE dbo.[Rates](
    [BuildNumber] [int] NOT NULL,
    [Tariff] [varchar](30) NOT NULL,
    [TariffStepName] [varchar](60) NOT NULL,
    [Factor1] [varchar](50) NOT NULL,
    [Factor2] [varchar](50) NULL,
    [Factor3] [varchar](50) NULL,
    [Factor4] [varchar](50) NULL,
    [Factor5] [varchar](50) NULL,
    [Factor6] [varchar](50) NULL,
    [Factor7] [varchar](50) NULL,
    [Factor8] [varchar](50) NULL,
    [Factor9] [varchar](50) NULL,
    [Factor10] [varchar](50) NULL,
    [Result1] [varchar](50) NULL,
    [Result2] [decimal](19, 6) NULL,
    [Result3] [decimal](19, 6) NULL,
    [Result4] [decimal](19, 6) NULL,
    [Result5] [decimal](19, 6) NULL,
    [Result6] [decimal](19, 6) NULL,
    [Result7] [decimal](19, 6) NULL,
    [Result8] [decimal](19, 6) NULL,
    [Result9] [decimal](19, 6) NULL,
    [Result10] [decimal](19, 6) NULL,
    [RateVersion] [decimal](18, 2) NULL,
    [SampleId] [int] NULL,
    [TextResult1] [varchar](50) NULL,
    [TextResult2] [varchar](50) NULL,
    [TextResult3] [varchar](50) NULL,
    [TextResult4] [varchar](50) NULL,
    [TextResult5] [varchar](50) NULL
)

编辑2: 对于那些想知道原因的人

陈述实际上是通过其他机制转换成这个样子的

exec sp_executesql N'select * from (select top 1 BuildNumber, RateVersion, SampleId, Tariff, TariffStepName, Factor1, Result1 from dbo.Rates
where Tariff = @Tariff and TariffStepName = @TariffStepName and (RateVersion <= @RV) and Factor1 = @Factor1 and (SampleId is null)
order by RateVersion desc, sampleId desc) top1 
',N'@Tariff varchar(50),@TariffStepName varchar(50),@RV decimal(3,2),@Factor1 bit',@Tariff='Default',@TariffStepName='I_P',@RV=1.00,@Factor1=0
go

如果选择的不是top 1而是紧接着它后面的行,那么在没有行的情况下,这将会失败并抛出错误,因为它不会转换为bit类型。

问题仍然存在:我该如何在即时窗口中调试编写SqlDataReader?


最后一件事,我猜想。query保存了什么?你能展示原始的SQL查询吗?也许错误就在查询本身。 - Zein Makki
@user3185569 查询在直接执行时(通过SQL管理工具)返回结果正常。 - Matas Vaitkevicius
这很令人困惑。我可以给你一个提示,就是逐个删除where条件,以检测哪个子句引起了错误。 - Zein Makki
您的查询是完全正确的。但我猜测您得到的错误是由于表中的实际数据造成的。在任何一列中都可能输入了错误的数据。如果您的“Rate”表不是太大,请检查其记录。我怀疑是“Factor1”和“Result1”列出了问题,但最好您检查所有列。 - Krishnraj Rana
看起来你在参数中使用了 new { ..., Factor1 = false, ... } 而不是 "false" - leppie
显示剩余3条评论
3个回答

3

如何在即时窗口调试时编写SqlDataReader?

SqlDataReader 实现接口 IDataReader。以下技巧适用于实现此接口的任何读取器。与 (new System.IO.StreamReader(stream)).ReadToEnd() 一样,这些技术将消耗数据读取器的内容,因此它将不再可用。

即时转储结果。

如果您没有时间准备并且需要立即查看读取器的内容,则可以将数据读取器加载到在即时窗口中定义的 DataTable 中,然后打印该表的 XML。

首先,在即时窗口中键入以下内容以定义三个运行时全局变量:

object [] _objs = null;
DataTable _table = null;
DataSet _set = null;

每次会话只需执行一次此操作。

接下来,如果代码已经开始读取表格列,你可以通过输入以下内容获取当前行的值:

_objs = new object[dataStream.FieldCount];
dataStream.GetValues(_objs);
_objs

当前值现在将会被显示。
然后,为了读取并显示其余的行,请执行以下操作:
_table = new DataTable();
_table.Load(dataStream);
_set = new DataSet();
_set.Tables.Add(_table);
_set.GetXml();
Debug.WriteLine(_set.GetXml());

你将会在Immediate Window中看到_set的内容以XML字符串形式打印出来。请注意,如果表被部分读取,DataTable.Load(IDataReader)将跳过当前行,所以请先转储当前值。
这对于与单个表相对应的读取器非常有效,但对于与多个组成集合的表相对应的读取器则不是很有效。
使用一个小型调试库转储结果。
如果你有一点时间准备或需要调试一个多表读取器,可以按照以下步骤进行。
首先,创建一个小型调试DLL项目,其中包含以下实用程序。你不需要将其链接到你实际调试的项目中。
namespace DataReaderDebugUtilities
{
    public static class DataReaderExtensions
    {
        public static object[] CurrentValues(this IDataReader reader)
        {
            if (reader == null)
                throw new ArgumentNullException();
            var objs = new object[reader.FieldCount];
            reader.GetValues(objs);
            return objs;
        }

        public static KeyValuePair<string, object> [] CurrentNamesAndValues(this IDataReader reader)
        {
            if (reader == null)
                throw new ArgumentNullException();
            var query = Enumerable.Range(0, reader.FieldCount).Select(i => new KeyValuePair<string, object>(reader.GetName(i), reader.GetValue(i)));
            return query.ToArray();
        }

        public static string ToStringAsDataTable(this IDataReader reader)
        {
            if (reader == null)
                throw new ArgumentNullException();
            var sb = new StringBuilder();
            using (var textWriter = new StringWriter(sb))
            using (var jsonWriter = new JsonTextWriter(textWriter) { Formatting = Formatting.Indented })
            {
                var serializer = JsonSerializer.CreateDefault();
                jsonWriter.WriteDataTable(reader, serializer);
            }
            return sb.ToString();
        }

        public static string ToStringAsDataSet(this IDataReader reader)
        {
            if (reader == null)
                throw new ArgumentNullException();
            var sb = new StringBuilder();
            using (var textWriter = new StringWriter(sb))
            using (var jsonWriter = new JsonTextWriter(textWriter) { Formatting = Formatting.Indented })
            {
                var serializer = JsonSerializer.CreateDefault();
                jsonWriter.WriteDataSet(reader, serializer);
            }
            return sb.ToString();
        }
    }

    public static class JsonExtensions
    {
        public static void WriteDataTable(this JsonWriter writer, IDataReader reader, JsonSerializer serializer)
        {
            if (writer == null || reader == null || serializer == null)
                throw new ArgumentNullException();
            writer.WriteStartArray();
            while (reader.Read())
            {
                writer.WriteStartObject();
                for (int i = 0; i < reader.FieldCount; i++)
                {
                    writer.WritePropertyName(reader.GetName(i));
                    serializer.Serialize(writer, reader[i]);
                }
                writer.WriteEndObject();
            }
            writer.WriteEndArray();
        }

        public static void WriteDataSet(this JsonWriter writer, IDataReader reader, JsonSerializer serializer)
        {
            if (writer == null || reader == null || serializer == null)
                throw new ArgumentNullException();
            writer.WriteStartObject();

            do
            {
                var tableName = string.Empty;
                var schemaTable = reader.GetSchemaTable();
                if (schemaTable != null)
                    tableName = schemaTable.Rows.Cast<DataRow>()
                        .Select(r => r[schemaTable.Columns[System.Data.Common.SchemaTableColumn.BaseTableName]].ToString())
                        .FirstOrDefault();
                writer.WritePropertyName(tableName ?? string.Empty);
                writer.WriteDataTable(reader, serializer);
            }
            while (reader.NextResult());

            writer.WriteEndObject();
        }
    }
}

(注意 - 获取表名的代码尚未经过完全测试。)
我正在使用 来序列化结果值并格式化整体结果。如果你喜欢,可以使用不同的序列化器。
以调试模式构建项目并将其复制到方便的位置,比如 C:\Temp\DataReaderDebugUtilities.dll
接下来,当你需要转储数据阅读器中的值时,在即时窗口中键入:
Assembly.LoadFile(@"C:\Temp\DataReaderDebugUtilities.dll");

现在,即使这个DLL没有链接到您的项目中,您也可以在立即窗口中调用其中的方法。因此,只需键入:
DataReaderDebugUtilities.DataReaderExtensions.CurrentNamesAndValues(dataStream)

如果有当前行,则会显示当前行的名称和值。

然后输入

string _s = DataReaderDebugUtilities.DataReaderExtensions.ToStringAsDataSet(dataStream);

string _s = DataReaderDebugUtilities.DataReaderExtensions.ToStringAsDataTable(dataStream);

将阅读器的剩余内容转储为数据表或数据集,以JSON字符串形式进行手动检查。


0
  • 你可以在 Dapper 代码内设置断点 - 它是开源项目。
  • Result1 被定义为 varchar(50),但你的 C# 类声明它是 decimal。

不错的发现,这意味着必须有某种转换机制,因为当我在C#中将Result1更改为字符串时,它会给出100多个错误,而当我检查SELECT distinct cast([Result1] as decimal(18,10)) FROM dbo.[Rates]时,甚至更好,它没有给我任何错误。此时,我将去与编写此代码的团队交谈(以及为什么),并了解其中的情况... - Matas Vaitkevicius

0

将 varchar 值 '0.01' 转换为数据类型 bit 时转换失败。
我认为这个消息是由 SQL Server 抛出的。因此应该在 SQL 级别上出现错误。尝试在 SQL Profiler 中检查实际查询并在 SSMS 中运行。


你的意思是说,如果你从分析器中复制粘贴查询语句到 SSMS 中运行,那么它就可以正常运行?我怀疑问题出在查询语句上,因为这个消息是在 SQL 层面上出现的(例如:select convert(bit,'0.01'))。 - par

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接