DbType.Time 已忽略 IDbDataParameter.DbType (SqlClient)
DbType.Time ignored for IDbDataParameter.DbType (SqlClient)
我有一个习惯,总是使用 System.Data
中的通用抽象,例如 IDbCommand
和 IDbDataParameter
来访问数据,而不是使用具体实现 System.Data.SqlClient.SqlDbCommand
和 System.Data.SqlClient.SqlParameter
,只要有可能,即使我的应用程序以 SQL 服务器为目标。我这样做是为了将来更容易将应用程序移植到另一个 RDBMS,或者根据需要在我的应用程序和 SQL 服务器之间引入数据库抽象层。十多年来,我一直在成功地使用这种模式。
然而,今天我遇到了一些非常奇怪的行为,其中使用 IDbDataParameter
的行为出乎意料。使用类似于这样的代码:
System.Data.IDbCommand cmd;
TimeSpan someTimeSpanValue;
...
System.Data.IDbDataParameter dataParameter = cmd.CreateParameter();
dataParameter.DbType = DbType.Time;
dataParameter.Value = someTimeSpanValue;
cmd.Execute();
ADO.NET 在我执行语句时抱怨它无法将我的 TimeSpan
转换为 DateTime
。基于此参数设置的基础数据库类型为 TIME
类型。很困惑,我跟踪了代码,果然,当我设置 dataParameter.DbType = DbType.Time
然后询问 dataParameter.DbType
时,它 returned DbType.DateTime
而不是像我设置的 DbType.Time
它到。
我有一个解决此代码的方法,如下所示:
dataParameter.DbType = v.DbType; //v.DbType is the desire DbType for the command being prepared
if (v.DbType == DbType.Time && dataParameter.DbType != DbType.Time && dataParameter is System.Data.SqlClient.SqlParameter sqlParam)
sqlParam.SqlDbType = SqlDbType.Time;
似乎当我将 SqlDbType 设置为 SqlDbType.Time,然后询问 IDbDataParameter.DbType
,它确实 return DbType.Time
并且一切正常,尽管这段代码现在不是数据库不可知论者,如我所愿。
更奇怪的是,如果将 IDbDataParameter.DbType
设置为 DbType.Time
(这是它的当前值)after 将 SqlDbType 设置为 SqlDbType.Time , 它仍然变为 DbType.DateTime
.
毕竟,我的问题是,这是否是 System.Data.SqlClient
对 IDbDataParameter
的实现中的一个缺陷,或者是否有一些我不知道的设置导致了这种行为(也许默认为 SQL 2008 之前的兼容性),我可以调整它以避免这种 hacky 解决方法?
感谢@DavidG 发表了引用 .NET 源代码的评论。基于参考代码:
override public DbType DbType {
get {
return GetMetaTypeOnly().DbType;
}
set {
MetaType metatype = _metaType;
if ((null == metatype) || (metatype.DbType != value) ||
// SQLBU 504029: Two special datetime cases for backward compat
// DbType.Date and DbType.Time should always be treated as setting DbType.DateTime instead
value == DbType.Date ||
value == DbType.Time) {
PropertyTypeChanging();
_metaType = MetaType.GetMetaTypeFromDbType(value);
}
}
}
我可以根据参考代码中的注释推断此行为是设计使然。
跟进 GetMetaTypeFromDbType(value)
reference source,这似乎是一致的:
internal static MetaType GetMetaTypeFromDbType(DbType target) {
// if we can't map it, we need to throw
switch (target) {
case DbType.AnsiString: return MetaVarChar;
case DbType.AnsiStringFixedLength: return MetaChar;
case DbType.Binary: return MetaVarBinary;
case DbType.Byte: return MetaTinyInt;
case DbType.Boolean: return MetaBit;
case DbType.Currency: return MetaMoney;
case DbType.Date:
case DbType.DateTime: return MetaDateTime;
case DbType.Decimal: return MetaDecimal;
case DbType.Double: return MetaFloat;
case DbType.Guid: return MetaUniqueId;
case DbType.Int16: return MetaSmallInt;
case DbType.Int32: return MetaInt;
case DbType.Int64: return MetaBigInt;
case DbType.Object: return MetaVariant;
case DbType.Single: return MetaReal;
case DbType.String: return MetaNVarChar;
case DbType.StringFixedLength: return MetaNChar;
case DbType.Time: return MetaDateTime;
case DbType.Xml: return MetaXml;
case DbType.DateTime2: return MetaDateTime2;
case DbType.DateTimeOffset: return MetaDateTimeOffset;
case DbType.SByte: // unsupported
case DbType.UInt16:
case DbType.UInt32:
case DbType.UInt64:
case DbType.VarNumeric:
default: throw ADP.DbTypeNotSupported(target, typeof(SqlDbType)); // no direct mapping, error out
}
}
基于此,SqlParameter
中 IDbDataParameter.DbType
的实现似乎根本不支持 DbType.Time
或任何创建类型 TIME
参数的方法IDbDataParameter
接口,因为 none 这些情况 return MetaTime
。我的结论是,获取参数以引用 TIME
类型的唯一方法是改用 SqlParameter.SqlDbType
。
我有一个习惯,总是使用 System.Data
中的通用抽象,例如 IDbCommand
和 IDbDataParameter
来访问数据,而不是使用具体实现 System.Data.SqlClient.SqlDbCommand
和 System.Data.SqlClient.SqlParameter
,只要有可能,即使我的应用程序以 SQL 服务器为目标。我这样做是为了将来更容易将应用程序移植到另一个 RDBMS,或者根据需要在我的应用程序和 SQL 服务器之间引入数据库抽象层。十多年来,我一直在成功地使用这种模式。
然而,今天我遇到了一些非常奇怪的行为,其中使用 IDbDataParameter
的行为出乎意料。使用类似于这样的代码:
System.Data.IDbCommand cmd;
TimeSpan someTimeSpanValue;
...
System.Data.IDbDataParameter dataParameter = cmd.CreateParameter();
dataParameter.DbType = DbType.Time;
dataParameter.Value = someTimeSpanValue;
cmd.Execute();
ADO.NET 在我执行语句时抱怨它无法将我的 TimeSpan
转换为 DateTime
。基于此参数设置的基础数据库类型为 TIME
类型。很困惑,我跟踪了代码,果然,当我设置 dataParameter.DbType = DbType.Time
然后询问 dataParameter.DbType
时,它 returned DbType.DateTime
而不是像我设置的 DbType.Time
它到。
我有一个解决此代码的方法,如下所示:
dataParameter.DbType = v.DbType; //v.DbType is the desire DbType for the command being prepared
if (v.DbType == DbType.Time && dataParameter.DbType != DbType.Time && dataParameter is System.Data.SqlClient.SqlParameter sqlParam)
sqlParam.SqlDbType = SqlDbType.Time;
似乎当我将 SqlDbType 设置为 SqlDbType.Time,然后询问 IDbDataParameter.DbType
,它确实 return DbType.Time
并且一切正常,尽管这段代码现在不是数据库不可知论者,如我所愿。
更奇怪的是,如果将 IDbDataParameter.DbType
设置为 DbType.Time
(这是它的当前值)after 将 SqlDbType 设置为 SqlDbType.Time , 它仍然变为 DbType.DateTime
.
毕竟,我的问题是,这是否是 System.Data.SqlClient
对 IDbDataParameter
的实现中的一个缺陷,或者是否有一些我不知道的设置导致了这种行为(也许默认为 SQL 2008 之前的兼容性),我可以调整它以避免这种 hacky 解决方法?
感谢@DavidG 发表了引用 .NET 源代码的评论。基于参考代码:
override public DbType DbType {
get {
return GetMetaTypeOnly().DbType;
}
set {
MetaType metatype = _metaType;
if ((null == metatype) || (metatype.DbType != value) ||
// SQLBU 504029: Two special datetime cases for backward compat
// DbType.Date and DbType.Time should always be treated as setting DbType.DateTime instead
value == DbType.Date ||
value == DbType.Time) {
PropertyTypeChanging();
_metaType = MetaType.GetMetaTypeFromDbType(value);
}
}
}
我可以根据参考代码中的注释推断此行为是设计使然。
跟进 GetMetaTypeFromDbType(value)
reference source,这似乎是一致的:
internal static MetaType GetMetaTypeFromDbType(DbType target) {
// if we can't map it, we need to throw
switch (target) {
case DbType.AnsiString: return MetaVarChar;
case DbType.AnsiStringFixedLength: return MetaChar;
case DbType.Binary: return MetaVarBinary;
case DbType.Byte: return MetaTinyInt;
case DbType.Boolean: return MetaBit;
case DbType.Currency: return MetaMoney;
case DbType.Date:
case DbType.DateTime: return MetaDateTime;
case DbType.Decimal: return MetaDecimal;
case DbType.Double: return MetaFloat;
case DbType.Guid: return MetaUniqueId;
case DbType.Int16: return MetaSmallInt;
case DbType.Int32: return MetaInt;
case DbType.Int64: return MetaBigInt;
case DbType.Object: return MetaVariant;
case DbType.Single: return MetaReal;
case DbType.String: return MetaNVarChar;
case DbType.StringFixedLength: return MetaNChar;
case DbType.Time: return MetaDateTime;
case DbType.Xml: return MetaXml;
case DbType.DateTime2: return MetaDateTime2;
case DbType.DateTimeOffset: return MetaDateTimeOffset;
case DbType.SByte: // unsupported
case DbType.UInt16:
case DbType.UInt32:
case DbType.UInt64:
case DbType.VarNumeric:
default: throw ADP.DbTypeNotSupported(target, typeof(SqlDbType)); // no direct mapping, error out
}
}
基于此,SqlParameter
中 IDbDataParameter.DbType
的实现似乎根本不支持 DbType.Time
或任何创建类型 TIME
参数的方法IDbDataParameter
接口,因为 none 这些情况 return MetaTime
。我的结论是,获取参数以引用 TIME
类型的唯一方法是改用 SqlParameter.SqlDbType
。