c# - 忽略 IDbDataParameter.DbType (SqlClient) 的 DbType.Time
问题描述
我有一个习惯,总是使用通用抽象System.Data
来访问数据,而不是具体的实现IDbCommand
,并且尽可能使用,即使我的应用程序以 SQL Server 为目标。我这样做是为了在将来更容易将应用程序移植到另一个 RDBMS,或者根据需要在我的应用程序和 SQL Server 之间引入数据库抽象层。十多年来,我一直在成功地使用这种模式。IDbDataParameter
System.Data.SqlClient.SqlDbCommand
System.Data.SqlClient.SqlParameter
然而,今天,我遇到了一些非常奇怪的行为,其中 usingIDbDataParameter
行为异常。使用与此类似的代码:
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
. 基于此参数设置的基础数据库类型是 type TIME
。很困惑,我跟踪了代码,果然,当我设置dataParameter.DbType = DbType.Time
然后询问dataParameter.DbType
时,它返回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,然后 interrogateIDbDataParameter.DbType
时,它确实返回DbType.Time
并且一切正常,尽管此代码现在不像我想要的那样与数据库无关。
更奇怪的是,如果在将 SqlDbType 设置为 SqlDbType.Time之后IDbDataParameter.DbType
设置为DbType.Time
(这是它的当前值),它仍然会变为.DbType.DateTime
毕竟,我的问题是,这是否是System.Data.SqlClient
's 的实现中的缺陷,IDbDataParameter
或者是否有一些我不知道的设置会导致这种行为(可能默认为 SQL 2008 之前的兼容性),我可以调整以避免这种情况骇客的解决方法?
解决方案
感谢@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)
参考源,看起来这是一致的:
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
}
}
Based on this, it would appear that implementation of IDbDataParameter.DbType
in SqlParameter
simply does not support DbType.Time
or any way to create a parameter of type TIME
using the IDbDataParameter
interface, since none of those cases return MetaTime
. I conclude that the only way to get a parameter to reference a TIME
type is to use the SqlParameter.SqlDbType
instead.
推荐阅读
- html - 如何在 css/Bootstrap 4 中对齐页脚项目?
- c# - ASP.NET 中的 SQL Server 数据缓存
- html - 如何使页脚留在页面底部引导程序 4
- stanford-nlp - 使用 NLP 从句子中提取信息
- java - 在java中逐位写入文件
- c# - 在 ASP.NET Core 应用程序中转换为 doc 文件并将 doc 文件附加到电子邮件
- python - 如何使 2 个版本的 Fasttext Python 包装器一起工作?
- python - 类型错误:构造函数返回 NULL
- java - 过滤4列listview android中的数据
- liquibase - 使用 loglevel=debug 在 liquibase 3.6.1 中日志记录不起作用