SQL跟踪/事件探查器-显示不明确的日期格式(.NET Core / EF)

时间:2019-01-15 11:41:24

标签: sql-server asp.net-core entity-framework-core

我们有一个使用英语(美国)安装的SQL Server 2008R2实例。 SSMS>实例>属性>常规。

我们使用默认语言“英国英语”设置了登录名。 SSMS>安全>登录>用户>属性。

.NET Core前端应用程序通过表单/ Web API生成插入语句(EF),该操作使用上面设置的登录名创建“ sp_executesql”系统存储过程。

使用SQL Server Profiler,我可以看到生成的插入SQL包含'yyyy-mm-dd'.格式的字符串日期,例如"2019-01-15 10:59:19.410"

在执行该语句之前,先执行exec sp_reset_connection,后接以下SET语句(我认为这与共享连接池有关,以确保每次登录的设置正确。)

-- network protocol: LPC
set quoted_identifier on
set arithabort off
set numeric_roundabort off
set ansi_warnings on
set ansi_padding on
set ansi_nulls on
set concat_null_yields_null on
set cursor_close_on_commit off
set implicit_transactions off
set language British
set dateformat dmy
set datefirst 1
set transaction isolation level read committed

让我感到困惑的是,即使环境设置为英式英语(DMY),尽管日期以yyyy-mm-dd格式存在,该语句仍能成功运行。例如-"2019-01-15 10:59:19.410"

我了解到,如果DMY是当前设置,则“ 2019-01-15 10:59:19.410”将被解释为2019年15月15日的第一天。

例如,这将失败,因为SQL会尝试将日期字符串解释为yyyy-dd-mm

SET LANGUAGE [British English];

SELECT
    CAST('2019-01-13' AS DATETIME);
  

消息242,级别16,状态3,第3行varchar数据的转换   类型转换为日期时间数据类型会导致值超出范围。

将语言设置为英语(us_english)会将字符串解释更改为MDY,并且上面的语句起作用。

我知道我可以使用不明确的日期字符串,但是我遇到的问题与EF前端生成的日期字符串有关。

我不知道为什么该语句成功运行。显然,存在从日期字符串到DATETIME类型的隐式强制转换。

如果我从SQL事件探查器复制该语句并将其粘贴到SSMS中,并尝试针对同一数据库/相同用户运行,则它将失败,如我所愿。

  

消息8114,级别16,状态1,第14行转换数据类型时出错   varchar转换为datetime。

所以...

我了解从字符串日期转换为DATETIME类型时发生了什么,并在SSMS中运行查询获得了一致的结果,但是我不明白为什么从前端运行SSMS中失败的查询的原因。

如果对此没有很好的解释,我深表歉意。

2 个答案:

答案 0 :(得分:4)

实际发生的情况是,Entity Framework生成参数化的语句,然后使用(二进制)TDS协议将其传递到服务器,这是应用程序与SQL Server进行通信的方式。作为RPC参数传递的DATETIME值不作为字符串传递-它们作为表示精确值的字节序列传递(例如,当2019-01-15T10:59:19.410作为a发送时,表示为D6 A9 00 00 AF 16 B5 00 DATETIME(两个小尾数整数)(免责声明:我实际上没有用数据包嗅探器检查过,因此我可能弄错了字节)。

当SQL Profiler将这些参数化语句视为文本批处理时,由于T-SQL没有DATETIME值的本机文本格式,因此将值重新格式化为字符串。不幸的是,它使用的格式不能保证在所有可能的语言设置下都可以往返,这在Profiler中应视为错误。它只缺少T,但这是一个重要的遗漏。

神秘的exec sp_reset_connection调用也会发生类似的情况,您会看到乱七八糟的所有使用连接池的应用程序的踪迹-没有应用程序实际生成这些调用,如果您尝试在Management Studio中执行此语句,您会将会看到没有sp_reset_connection过程。在协议级别上,数据包头中只有一个“重置此连接”位,当将其表示为语句或事件跟踪时,该位将转换为包含代表当前选项的虚拟SET语句的虚拟调用。 / p>

简而言之:您的应用程序运行良好,但是如果要准确重放它们在进行调用时发生的情况,则复制粘贴Profiler输出将无效。

答案 1 :(得分:0)

我偶然发现了一个非常清晰的博客-https://weblogs.sqlteam.com/dang/2007/10/11/sql-trace-parameter-values-are-not-always-as-they-seem/

以上链接中的图像不再可用(最初于2007年发布)。

但是该文本非常有用,在完全消失之前,我在下面粘贴了该文本。

我希望这对某人有帮助。

  

Dan Guzman博客

     

SQL跟踪参数值并不总是看起来像Dan Guzman在2007年10月11日星期四那样

     

我偶然发现了我认为是令人惊讶的SQL Trace / Profiler行为   值得一提。跟踪RPC中报告的参数值   开始/完成事件不是SQL Server用于执行以下操作的值   执行查询。这是我最近发现的一个示例,该示例显示   这种行为。我运行以下C#代码以执行参数化   查询参数值设置为10月11日的“ SELECT @DateTime”   2007。控制台消息验证SQL Server返回了预期的日期。

     

我从跟踪中粘贴了脚本并从SQL Server中运行了该脚本   Management Studio查询窗口。这是SSMS脚本的痕迹   执行:

     

即使SQL看起来相同,SSMS查询也会返回一个   与申请日期(“十月”)不同的日期(“ 2007年11月10日”)   2007年11月11日”)!这是怎么回事?为什么相同的脚本会返回   从应用程序代码与Management Studio运行时,它们的值不同   (或查询分析器),甚至具有相同的会话设置?

     

行为差异的原因是跟踪事件类。的   应用程序跟踪显示了RPC:Completed事件,但SSMS会话   跟踪显示SQL:BatchCompleted。两者都有相同的“执行   sp_executesql…”语句在TextData中。即使痕迹   为两个事件报告了相同的TextData,SQL Server处理了   RPC与SQL批处理不同,跟踪没有显示   RPC事件的完整故事。

     

问题是RPC事件的datetime输入参数值   并没有像跟踪那样真正包含在SQL语句中   显示RPC:已完成事件。实际的datetime参数值   在RPC执行期间使用的SQL Server是通过本机传递的(即   二进制)格式通过低级TDS协议。跟踪TextData是   只是对该值进行反向工程的字符串表示,而不是   比实际使用的SQL Server值高。

     

相比之下,SSMS发送的批处理没有参数化,因此SQL Server   需要解析批处理文本中的datetime字符串值。的   datetime字符串“ 2007-10-11 00:00:00:000”(由跟踪生成,不是   由应用程序传递)是模棱两可的,并根据情况进行了不同的解释   在DATEFORMAT设置上。由于设置了“ DATEFORMAT DMY”   脚本,日期字符串值被(错误)解释为11月10日,   2007年而不是2007年10月11日。DATEFORMAT设置没有   因为设置会影响RPC(参数化值)   仅字符串解析,而不是   应用。

     

我在帮助正在使用的用户时发现了此问题   法语SQL Server(这就是为什么我指定DATEFORMAT DMY的原因)   解决datetime参数问题。跟踪数据使我失望   错误的路径,因为它没有立即与我点击1)   DATEFORMAT不会影响RPC日期时间参数,并且2)跟踪RPC   无论如何,TextData都不用于查询执行。我提交了Connect   对此的反馈,建议将SQL跟踪/配置器更改为   以DATEFORMAT中性格式对RPC日期时间值进行序列化,例如   “ 2007-10-11T00:00:00:000”或“ 20071011 00:00:00:000”。这将   当跟踪TextData SQL执行为   批处理,并且在分析跟踪数据时更加直观。

     

版权所有©2018-Dan Guzman