将 SqlDataReader 写入立即数 window c#

Write SqlDataReader to immediate window c#

我正在尝试调试抛出错误的 SQL 响应:

Conversion failed when converting the varchar value '0.01' to data type bit.

这没有多大意义,因为对象没有任何布尔值。

代码:

 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

此时我可以访问 dataStreamSqlDataReader

我正在寻找一种从 SqlDataReader 输出 'raw' 结果的方法,比如

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
)

EDIT2: 对于那些想知道原因是什么的人

声明实际上是通过其他机制转换成这个的

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 window?

  • 您可以在 Dapper 代码中设置断点 - 是开源的。
  • Result1 被定义为 varchar(50),但是你的 C# class 说是十进制。

Conversion failed when converting the varchar value '0.01' to data type bit.
我认为消息是由 SQL 服务器抛出的。所以 SQL 级别应该有错误。尝试在 SQL 探查器和 SSMS 运行 中检查实际查询。

即时调试时如何编写 SqlDataReader window?

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

纯粹即时转储结果。

如果您没有时间准备并且需要立即查看您的 reader 的内容,您可以将数据 reader 加载到 DataTable 中定义的直接 window,他们打印出 table.

的 XML

首先,在window中定义三个运行时间的全局变量,输入:

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

每个会话执行一次。

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

_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());

您将看到 _set 的内容在立即 Window 中打印为 XML 字符串。请注意,如果 table 被部分读取,DataTable.Load(IDataReader) 将跳过当前行,因此首先转储当前值。

这适用于对应于单个 table 的 reader,但不适用于对应于多个 table 的 reader 形成一个集合。

使用小型调试库转储结果。

如果你有一点时间准备或者需要调试多tablereader,你可以进行以下操作。

首先,使用如下实用程序创建一个小型调试 DLL 项目。 您不需要将此 link 放入您实际正在调试的项目中。

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();
        }
    }
}

(注意 - 获取 table 名称的代码未经过全面测试。)

注意我正在使用 序列化结果值并格式化整体结果。如果您愿意,可以使用不同的序列化程序。

在调试模式下构建项目并将其复制到方便的位置,比如 C:\Temp\DataReaderDebugUtilities.dll

接下来,当您需要转储数据 reader 中的值时,在立即 window 中键入:

Assembly.LoadFile(@"C:\Temp\DataReaderDebugUtilities.dll");

现在您可以立即 window 从这个 DLL 中调用方法,即使它没有 link 到您的项目中。因此键入:

DataReaderDebugUtilities.DataReaderExtensions.CurrentNamesAndValues(dataStream)

将显示当前行的名称和值(如果有)。

然后输入

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

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

会将reader的剩余内容作为数据table或数据集转储到JSON字符串中以供人工检查。