在将对象设置为参数值之前将对象变量转换为 DBType?
Convert a object variable to a DBType before setting object as value of a parameter?
我正在使用 SqlCommand 执行存储过程。使用classSqlCommandBuilder
的DeriveParameters
方法,根据存储过程的定义自动创建参数。这会自动为我设置 DbType。接下来,我使用 <string, object>
键值对遍历字典,其中字符串是参数的名称,对象包含要设置的值。
简化示例来源:
public DataTable FetchProducts(SqlConnection sqlConn, IDictionary<string, object> paramvalues)
{
using (SqlCommand cmd = new SqlCommand("ProcFetchProducts", sqlConn))
{
cmd.CommandType = CommandType.StoredProcedure;
SqlCommandBuilder.DeriveParameters(cmd);
foreach (KeyValuePair<string, object> pair in paramvalues)
{
var index = cmd.Parameters.IndexOf(pair.Key);
cmd.Parameters[index].Value = pair.Value;
}
using (var dr = cmd.ExecuteReader())
{
var dt = new DataTable("Result");
dt.Load(dr);
return dt;
}
}
}
有时对象包含的值与参数的 DBType 不匹配。例如,一个参数是 smallint 类型,对象包含一个字符串。现在,当我执行数据读取器时,我得到一个 "input string is not in a correct format" FormatException
,它没有告诉我哪个参数导致了这个问题。
所以我的主要问题是:有没有办法将对象从字典转换为参数中定义的 DBType,这样我就可以在执行数据读取器之前检查它是否是正确的类型?
没有内置函数,但您可以创建自己的简单列表并检查它们是否匹配:
typeMap = new Dictionary<Type, DbType>();
typeMap[typeof(byte)] = DbType.Byte;
typeMap[typeof(sbyte)] = DbType.SByte;
typeMap[typeof(short)] = DbType.Int16;
typeMap[typeof(ushort)] = DbType.UInt16;
typeMap[typeof(int)] = DbType.Int32;
typeMap[typeof(uint)] = DbType.UInt32;
typeMap[typeof(long)] = DbType.Int64;
typeMap[typeof(ulong)] = DbType.UInt64;
typeMap[typeof(float)] = DbType.Single;
typeMap[typeof(double)] = DbType.Double;
typeMap[typeof(decimal)] = DbType.Decimal;
typeMap[typeof(bool)] = DbType.Boolean;
typeMap[typeof(string)] = DbType.String;
typeMap[typeof(char)] = DbType.StringFixedLength;
typeMap[typeof(Guid)] = DbType.Guid;
typeMap[typeof(DateTime)] = DbType.DateTime;
typeMap[typeof(DateTimeOffset)] = DbType.DateTimeOffset;
typeMap[typeof(byte[])] = DbType.Binary;
typeMap[typeof(byte?)] = DbType.Byte;
typeMap[typeof(sbyte?)] = DbType.SByte;
typeMap[typeof(short?)] = DbType.Int16;
typeMap[typeof(ushort?)] = DbType.UInt16;
typeMap[typeof(int?)] = DbType.Int32;
typeMap[typeof(uint?)] = DbType.UInt32;
typeMap[typeof(long?)] = DbType.Int64;
typeMap[typeof(ulong?)] = DbType.UInt64;
typeMap[typeof(float?)] = DbType.Single;
typeMap[typeof(double?)] = DbType.Double;
typeMap[typeof(decimal?)] = DbType.Decimal;
typeMap[typeof(bool?)] = DbType.Boolean;
typeMap[typeof(char?)] = DbType.StringFixedLength;
typeMap[typeof(Guid?)] = DbType.Guid;
typeMap[typeof(DateTime?)] = DbType.DateTime;
typeMap[typeof(DateTimeOffset?)] = DbType.DateTimeOffset;
typeMap[typeof(System.Data.Linq.Binary)] = DbType.Binary;
只需调用 GetType
您的 KVP 值,如下所示:
foreach (KeyValuePair<string, object> pair in paramvalues)
{
var index = cmd.Parameters.IndexOf(pair.Key);
cmd.Parameters[index].Value = pair.Value;
// If null, you should use DbNull.
var YOUR_TYPE = typeMap[pair.Value.GetType()];
}
列表的致谢名单:
正如一些提示,我更喜欢 var
而不是 KeyValuePair<string, object>
,但这对你的问题并不重要 :)
已更新
======在OP澄清后更新了答案(见评论)=======
您需要维护 CLR 类型映射列表 w.r.t。 SqlDbType 然后检查它是否是字符串类型和 parse/convert 字符串类型到相应的 clr 类型和 return 它作为对象。这会将基础类型从字符串更改为 SqlDbType 的映射 clr 类型。
SqlDbType 到 CLR 类型:(referred from this source)
public static Type GetClrType(SqlDbType sqlType)
{
switch (sqlType)
{
case SqlDbType.BigInt:
return typeof(long?);
case SqlDbType.Binary:
case SqlDbType.Image:
case SqlDbType.Timestamp:
case SqlDbType.VarBinary:
return typeof(byte[]);
case SqlDbType.Bit:
return typeof(bool?);
case SqlDbType.Char:
case SqlDbType.NChar:
case SqlDbType.NText:
case SqlDbType.NVarChar:
case SqlDbType.Text:
case SqlDbType.VarChar:
case SqlDbType.Xml:
return typeof(string);
case SqlDbType.DateTime:
case SqlDbType.SmallDateTime:
case SqlDbType.Date:
case SqlDbType.Time:
case SqlDbType.DateTime2:
return typeof(DateTime?);
case SqlDbType.Decimal:
case SqlDbType.Money:
case SqlDbType.SmallMoney:
return typeof(decimal?);
case SqlDbType.Float:
return typeof(double?);
case SqlDbType.Int:
return typeof(int?);
case SqlDbType.Real:
return typeof(float?);
case SqlDbType.UniqueIdentifier:
return typeof(Guid?);
case SqlDbType.SmallInt:
return typeof(short?);
case SqlDbType.TinyInt:
return typeof(byte?);
case SqlDbType.Variant:
case SqlDbType.Udt:
return typeof(object);
case SqlDbType.Structured:
return typeof(DataTable);
case SqlDbType.DateTimeOffset:
return typeof(DateTimeOffset?);
default:
throw new ArgumentOutOfRangeException("sqlType");
}
}
字符串类型转换器:
private static object Convert(string value, Type type)
{
object result;
if (string.IsNullOrWhiteSpace(value))
{
return null;
}
try
{
var converter = TypeDescriptor.GetConverter(type);
result = converter.ConvertFromString(value);
return result;
}
catch (Exception exception)
{
// Log this exception if required.
throw new InvalidCastException(string.Format("Unable to cast the {0} to type {1}", value, newType, exception));
}
}
用法:
foreach (KeyValuePair<string, object> pair in paramvalues)
{
var index = cmd.Parameters.IndexOf(pair.Key);
var value = pair.Value;
if (pair.Value == typeof(string))
{
value = Convert((string)pair.Value, GetClrType(cmd.Parameters[index].SqlDbType));
}
cmd.Parameters[index].Value = value;
}
感谢@BendEg 和@Vendettamit,我找到了解决方案。我根据他们的示例代码创建了一个字典 Dictionary<DbType, Type>()
,并将每个 DbType 映射到一个 Clr 类型。一个简单的 GetClrType
方法从字典中获取 clr 类型。接下来,我尝试转换它。如果失败,我会捕获异常并向用户报告转换失败并且参数的值类型错误。
Type clrType = SqlDbTypeResolver.GetClrType(cmd.Parameters[index].DbType);
try
{
Convert.ChangeType(parm.Value, clrType); // no need to store the value, I just need to catch the exception if thrown.
}
catch(SomeException ex)
{
//report stuff to user about failed conversion
}
我正在使用 SqlCommand 执行存储过程。使用classSqlCommandBuilder
的DeriveParameters
方法,根据存储过程的定义自动创建参数。这会自动为我设置 DbType。接下来,我使用 <string, object>
键值对遍历字典,其中字符串是参数的名称,对象包含要设置的值。
简化示例来源:
public DataTable FetchProducts(SqlConnection sqlConn, IDictionary<string, object> paramvalues)
{
using (SqlCommand cmd = new SqlCommand("ProcFetchProducts", sqlConn))
{
cmd.CommandType = CommandType.StoredProcedure;
SqlCommandBuilder.DeriveParameters(cmd);
foreach (KeyValuePair<string, object> pair in paramvalues)
{
var index = cmd.Parameters.IndexOf(pair.Key);
cmd.Parameters[index].Value = pair.Value;
}
using (var dr = cmd.ExecuteReader())
{
var dt = new DataTable("Result");
dt.Load(dr);
return dt;
}
}
}
有时对象包含的值与参数的 DBType 不匹配。例如,一个参数是 smallint 类型,对象包含一个字符串。现在,当我执行数据读取器时,我得到一个 "input string is not in a correct format" FormatException
,它没有告诉我哪个参数导致了这个问题。
所以我的主要问题是:有没有办法将对象从字典转换为参数中定义的 DBType,这样我就可以在执行数据读取器之前检查它是否是正确的类型?
没有内置函数,但您可以创建自己的简单列表并检查它们是否匹配:
typeMap = new Dictionary<Type, DbType>();
typeMap[typeof(byte)] = DbType.Byte;
typeMap[typeof(sbyte)] = DbType.SByte;
typeMap[typeof(short)] = DbType.Int16;
typeMap[typeof(ushort)] = DbType.UInt16;
typeMap[typeof(int)] = DbType.Int32;
typeMap[typeof(uint)] = DbType.UInt32;
typeMap[typeof(long)] = DbType.Int64;
typeMap[typeof(ulong)] = DbType.UInt64;
typeMap[typeof(float)] = DbType.Single;
typeMap[typeof(double)] = DbType.Double;
typeMap[typeof(decimal)] = DbType.Decimal;
typeMap[typeof(bool)] = DbType.Boolean;
typeMap[typeof(string)] = DbType.String;
typeMap[typeof(char)] = DbType.StringFixedLength;
typeMap[typeof(Guid)] = DbType.Guid;
typeMap[typeof(DateTime)] = DbType.DateTime;
typeMap[typeof(DateTimeOffset)] = DbType.DateTimeOffset;
typeMap[typeof(byte[])] = DbType.Binary;
typeMap[typeof(byte?)] = DbType.Byte;
typeMap[typeof(sbyte?)] = DbType.SByte;
typeMap[typeof(short?)] = DbType.Int16;
typeMap[typeof(ushort?)] = DbType.UInt16;
typeMap[typeof(int?)] = DbType.Int32;
typeMap[typeof(uint?)] = DbType.UInt32;
typeMap[typeof(long?)] = DbType.Int64;
typeMap[typeof(ulong?)] = DbType.UInt64;
typeMap[typeof(float?)] = DbType.Single;
typeMap[typeof(double?)] = DbType.Double;
typeMap[typeof(decimal?)] = DbType.Decimal;
typeMap[typeof(bool?)] = DbType.Boolean;
typeMap[typeof(char?)] = DbType.StringFixedLength;
typeMap[typeof(Guid?)] = DbType.Guid;
typeMap[typeof(DateTime?)] = DbType.DateTime;
typeMap[typeof(DateTimeOffset?)] = DbType.DateTimeOffset;
typeMap[typeof(System.Data.Linq.Binary)] = DbType.Binary;
只需调用 GetType
您的 KVP 值,如下所示:
foreach (KeyValuePair<string, object> pair in paramvalues)
{
var index = cmd.Parameters.IndexOf(pair.Key);
cmd.Parameters[index].Value = pair.Value;
// If null, you should use DbNull.
var YOUR_TYPE = typeMap[pair.Value.GetType()];
}
列表的致谢名单:
正如一些提示,我更喜欢 var
而不是 KeyValuePair<string, object>
,但这对你的问题并不重要 :)
已更新
======在OP澄清后更新了答案(见评论)=======
您需要维护 CLR 类型映射列表 w.r.t。 SqlDbType 然后检查它是否是字符串类型和 parse/convert 字符串类型到相应的 clr 类型和 return 它作为对象。这会将基础类型从字符串更改为 SqlDbType 的映射 clr 类型。
SqlDbType 到 CLR 类型:(referred from this source)
public static Type GetClrType(SqlDbType sqlType)
{
switch (sqlType)
{
case SqlDbType.BigInt:
return typeof(long?);
case SqlDbType.Binary:
case SqlDbType.Image:
case SqlDbType.Timestamp:
case SqlDbType.VarBinary:
return typeof(byte[]);
case SqlDbType.Bit:
return typeof(bool?);
case SqlDbType.Char:
case SqlDbType.NChar:
case SqlDbType.NText:
case SqlDbType.NVarChar:
case SqlDbType.Text:
case SqlDbType.VarChar:
case SqlDbType.Xml:
return typeof(string);
case SqlDbType.DateTime:
case SqlDbType.SmallDateTime:
case SqlDbType.Date:
case SqlDbType.Time:
case SqlDbType.DateTime2:
return typeof(DateTime?);
case SqlDbType.Decimal:
case SqlDbType.Money:
case SqlDbType.SmallMoney:
return typeof(decimal?);
case SqlDbType.Float:
return typeof(double?);
case SqlDbType.Int:
return typeof(int?);
case SqlDbType.Real:
return typeof(float?);
case SqlDbType.UniqueIdentifier:
return typeof(Guid?);
case SqlDbType.SmallInt:
return typeof(short?);
case SqlDbType.TinyInt:
return typeof(byte?);
case SqlDbType.Variant:
case SqlDbType.Udt:
return typeof(object);
case SqlDbType.Structured:
return typeof(DataTable);
case SqlDbType.DateTimeOffset:
return typeof(DateTimeOffset?);
default:
throw new ArgumentOutOfRangeException("sqlType");
}
}
字符串类型转换器:
private static object Convert(string value, Type type)
{
object result;
if (string.IsNullOrWhiteSpace(value))
{
return null;
}
try
{
var converter = TypeDescriptor.GetConverter(type);
result = converter.ConvertFromString(value);
return result;
}
catch (Exception exception)
{
// Log this exception if required.
throw new InvalidCastException(string.Format("Unable to cast the {0} to type {1}", value, newType, exception));
}
}
用法:
foreach (KeyValuePair<string, object> pair in paramvalues)
{
var index = cmd.Parameters.IndexOf(pair.Key);
var value = pair.Value;
if (pair.Value == typeof(string))
{
value = Convert((string)pair.Value, GetClrType(cmd.Parameters[index].SqlDbType));
}
cmd.Parameters[index].Value = value;
}
感谢@BendEg 和@Vendettamit,我找到了解决方案。我根据他们的示例代码创建了一个字典 Dictionary<DbType, Type>()
,并将每个 DbType 映射到一个 Clr 类型。一个简单的 GetClrType
方法从字典中获取 clr 类型。接下来,我尝试转换它。如果失败,我会捕获异常并向用户报告转换失败并且参数的值类型错误。
Type clrType = SqlDbTypeResolver.GetClrType(cmd.Parameters[index].DbType);
try
{
Convert.ChangeType(parm.Value, clrType); // no need to store the value, I just need to catch the exception if thrown.
}
catch(SomeException ex)
{
//report stuff to user about failed conversion
}