我正在尝试调试一个抛出错误的SQL响应:
将varchar值"0.01"转换为数据类型位时,转换失败。
这没有多大意义,因为对象没有任何bool
代码: 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 statobj, out bool dataReady) + 0x1ce1 bytes
此时我可以访问dataStream
也就是SqlDataReader
我正在寻找一种方法来输出'原始'结果的SqlDataReader
,像
System.Diagnostics.Debug.WriteLine((new System.IO.StreamReader(stream)).ReadToEnd());
but for 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时,调试到即时窗口?
我如何写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
的内容以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:TempDataReaderDebugUtilities.dll
。
接下来,当需要将值转储到数据读取器中时,在直接窗口中键入:
Assembly.LoadFile(@"C:TempDataReaderDebugUtilities.dll");
现在您可以在直接窗口中从这个DLL调用方法,即使它没有链接到您的项目中。因此输入:
DataReaderDebugUtilities.DataReaderExtensions.CurrentNamesAndValues(dataStream)
将显示当前行的名称和值(如果有的话)。
然后打字
string _s = DataReaderDebugUtilities.DataReaderExtensions.ToStringAsDataSet(dataStream);
或
string _s = DataReaderDebugUtilities.DataReaderExtensions.ToStringAsDataTable(dataStream);
将读取器的剩余内容作为数据表或数据集转储到JSON字符串中,以供手动检查。
- 你可以在Dapper代码中设置断点-是开源的。
- Result1被定义为varchar(50),但是你的c#类说的是十进制。
Conversion failed when converting the varchar value '0.01' to data type bit.
我认为消息是由SQL服务器抛出的。所以在SQL级别应该有错误。