Напишите SqlDataReader в ближайшее окно C#

Я пытаюсь отладить ответ SQL, который выдает ошибку:

Ошибка преобразования при преобразовании значения varchar '0.01' в бит типа данных.

Это не имеет большого смысла, так как объект не имеет никаких bools.

Код:

 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. bulkCopyHandler, System.Data.SqlClient.TdsParserStateObject stateObj, out bool dataReady) + 0x1ce1 байт

На данный момент у меня есть доступ к dataStream который является SqlDataReader

Я ищу способ вывести "сырой" результат прямо из 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
)

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 как будто это было задумано, но ряд после этого не бросил бы в бит

Вопрос все еще стоит: как мне написать SqlDataReader при отладке на лету в непосредственное окно?

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

Вы увидите содержимое _set распечатывается в Immediate Window в виде строки 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();
        }
    }
}

(Примечание - код для получения имени таблицы не полностью протестирован.)

Примечание. Я использую json.net для сериализации значений результатов и форматирования общих результатов. Вы можете использовать другой сериализатор, если хотите.

Создайте проект в режиме отладки и скопируйте его в удобное место, скажем 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 для ручной проверки.

  • Вы можете установить точку останова внутри кода Dapper - это Open Source.
  • Result1 определяется как varchar(50), но ваш класс C# имеет десятичное значение.

Conversion failed when converting the varchar value '0.01' to data type bit.
Я думаю, что сообщение выбрасывается сервером SQL. поэтому должна быть ошибка на уровне SQL. Попробуйте проверить фактический запрос в профилировщике SQL и запустить в SSMS.

Другие вопросы по тегам