我正在寻找.Net System.Type和SqlDbType之间的智能转换。我发现它是以下想法:
private static SqlDbType TypeToSqlDbType(Type t)
{
String name = t.Name;
SqlDbType val = SqlDbType.VarChar; // default value
try
{
if (name.Contains("16") || name.Contains("32") || name.Contains("64"))
{
name = name.Substring(0, name.Length - 2);
}
val = (SqlDbType)Enum.Parse(typeof(SqlDbType), name, true);
}
catch (Exception)
{
// add error handling to suit your taste
}
return val;
}
上面的代码不是很好,是一个代码气味,这就是为什么我写了以下,天真,不聪明,但有用的功能,基于https://msdn.microsoft.com/en-us/library/cc716729(v=vs.110).aspx:
public static SqlDbType ConvertiTipo(Type giveType)
{
var typeMap = new Dictionary<Type, SqlDbType>();
typeMap[typeof(string)] = SqlDbType.NVarChar;
typeMap[typeof(char[])] = SqlDbType.NVarChar;
typeMap[typeof(int)] = SqlDbType.Int;
typeMap[typeof(Int32)] = SqlDbType.Int;
typeMap[typeof(Int16)] = SqlDbType.SmallInt;
typeMap[typeof(Int64)] = SqlDbType.BigInt;
typeMap[typeof(Byte[])] = SqlDbType.VarBinary;
typeMap[typeof(Boolean)] = SqlDbType.Bit;
typeMap[typeof(DateTime)] = SqlDbType.DateTime2;
typeMap[typeof(DateTimeOffset)] = SqlDbType.DateTimeOffset;
typeMap[typeof(Decimal)] = SqlDbType.Decimal;
typeMap[typeof(Double)] = SqlDbType.Float;
typeMap[typeof(Decimal)] = SqlDbType.Money;
typeMap[typeof(Byte)] = SqlDbType.TinyInt;
typeMap[typeof(TimeSpan)] = SqlDbType.Time;
return typeMap[(giveType)];
}
有人知道如何以更清洁,更好,更好的方式获得相同的结果吗?
你的方法是一个良好的开端,但填充该字典应该只进行一次,正如伊恩在评论中所说。
这里有一个GIST基于相同的想法,虽然它不会在相同的类型集之间进行转换:https://gist.github.com/abrahamjp/858392
警告
我在下面有a working example,但你需要知道这种方法确实有一些问题。例如:
string
,你如何在Char
,NChar
,VarChar
,NVarChar
,Text
或NText
(甚至Xml
,也许)之间选择正确的?byte[]
这样的blob,你应该使用Binary
,VarBinary
还是Image
?decimal
,float
和double
,你应该去Decimal
,Float
,Money
,SmallMoney
或Real
吗?DateTime
,你需要DateTime2
,DateTimeOffset
,DateTime
或SmallDateTime
吗?Nullable
类型,如int?
?那些应该最有可能给出与基础类型相同的SqlDbType
。此外,只提供Type
不会告诉你任何其他约束,如字段大小和精度。做出正确的决定还涉及如何在您的应用程序中使用数据以及如何将数据存储在数据库中。
最好的办法是让ORM为你做这件事。
码
public static class SqlHelper
{
private static Dictionary<Type, SqlDbType> typeMap;
// Create and populate the dictionary in the static constructor
static SqlHelper()
{
typeMap = new Dictionary<Type, SqlDbType>();
typeMap[typeof(string)] = SqlDbType.NVarChar;
typeMap[typeof(char[])] = SqlDbType.NVarChar;
typeMap[typeof(byte)] = SqlDbType.TinyInt;
typeMap[typeof(short)] = SqlDbType.SmallInt;
typeMap[typeof(int)] = SqlDbType.Int;
typeMap[typeof(long)] = SqlDbType.BigInt;
typeMap[typeof(byte[])] = SqlDbType.Image;
typeMap[typeof(bool)] = SqlDbType.Bit;
typeMap[typeof(DateTime)] = SqlDbType.DateTime2;
typeMap[typeof(DateTimeOffset)] = SqlDbType.DateTimeOffset;
typeMap[typeof(decimal)] = SqlDbType.Money;
typeMap[typeof(float)] = SqlDbType.Real;
typeMap[typeof(double)] = SqlDbType.Float;
typeMap[typeof(TimeSpan)] = SqlDbType.Time;
/* ... and so on ... */
}
// Non-generic argument-based method
public static SqlDbType GetDbType(Type giveType)
{
// Allow nullable types to be handled
giveType = Nullable.GetUnderlyingType(giveType) ?? giveType;
if (typeMap.ContainsKey(giveType))
{
return typeMap[giveType];
}
throw new ArgumentException($"{giveType.FullName} is not a supported .NET class");
}
// Generic version
public static SqlDbType GetDbType<T>()
{
return GetDbType(typeof(T));
}
}
这就是你如何使用它:
var sqlDbType = SqlHelper.GetDbType<string>();
// or:
var sqlDbType = SqlHelper.GetDbType(typeof(DateTime?));
// or:
var sqlDbType = SqlHelper.GetDbType(property.PropertyType);
似乎这种查找表已经可用,虽然不是在System.Data
(或.Object
或.Type
)中,而是在System.Web中。
项目 - >添加参考 - > System.Web - >确定
然后https://msdn.microsoft.com/en-us/library/system.data.sqldbtype(v=vs.110).aspx也说
设置命令参数时,链接SqlDbType和DbType。因此,设置DbType会将SqlDbType更改为支持的SqlDbType。
所以,理论上这应该有效;)
using Microsoft.SqlServer.Server; // SqlDataRecord and SqlMetaData
using System;
using System.Collections; // IEnumerator and IEnumerable
using System.Collections.Generic; // general IEnumerable and IEnumerator
using System.Data; // DataTable and SqlDataType
using System.Data.SqlClient; // SqlConnection, SqlCommand, and SqlParameter
using System.Web.UI.WebControls; // for Parameters.Convert... functions
private static SqlDbType TypeToSqlDbType(Type t) {
DbType dbtc = Parameters.ConvertTypeCodeToDbType(t.GetTypeCodeImpl());
SqlParameter sp = new SqlParameter();
// DbParameter dp = new DbParameter();
// dp.DbType = dbtc;
sp.DbType = dbtc;
return sp.SqlDbType;
}
编辑:我在想,这适用于System.Data.SqlTypes类型。我会留在这里,以防万一将来帮助某人。
我做这样的事情:
object objDbValue = DbReader.GetValue(columnIndex);
Type sqlType = DbReader.GetFieldType(columnIndex);
Type clrType = null;
if (sqlType.Name.StartsWith("Sql"))
{
var objClrValue = objDbValue.GetType()
.GetProperty("Value")
.GetValue(objDbValue, null);
clrType = objClrValue.GetType();
}
因为每个SqlDbType都有一个.Value属性,它是实际的底层CLR类型,所以我使用反射来获取它。太糟糕了SqlDbType没有一些接口可以保存这个.Value属性,并且不需要反射。 它并不完美,但您不必手动创建,维护或填充字典。您可以在现有的dict中查找类型,如果它不存在,请使用upper方法自动添加映射。几乎是自动生成的。 还要处理SQL Server将来可能会收到的任何新类型。
我的办公室伙伴给了我一个尝试SqlParameter属性的想法:
Func<Object, SqlDbType> getSqlType = val => new SqlParameter("Test", val).SqlDbType;
Func<Type, SqlDbType> getSqlType2 = type => new SqlParameter("Test", type.IsValueType?Activator.CreateInstance(type):null).SqlDbType;
//returns nvarchar...
Object obj = "valueToTest";
getSqlType(obj).Dump();
getSqlType2(typeof(String)).Dump();
//returns int...
obj = 4;
getSqlType(obj).Dump();
getSqlType2(typeof(Int32)).Dump();
//returns bigint...
obj = Int64.MaxValue;
getSqlType(obj).Dump();
getSqlType2(typeof(Int64)).Dump();