我在SQL过程上方创建了“通用”包装器,并且可以解析所有必需参数的名称和sqltypes,但是有什么方法可以使它成为“基础” .NET类型?
我的目标是做类似的事情:
SqlParameter param; object value; object correctParam = param.GetNETType().GetMethod("Parse", new Type[] { typeof(string) }).Invoke(value.ToString()); param.Value = correctParam;
我需要的是GetNETType。我知道可以将其编写为param.SqlDbType内的开关,但这是一种较短的方法,而较短的注释代码意味着较低的维护:)
不幸的是,据我所知,.NET Framework内部的代码未公开此映射。在此之前,我已经浏览了.NET Framework参考源,发现在.NET代码中有很多长的每个类型的switch语句,就像您要避免的那样,但是似乎都没有暴露在外部。
如果您真的只想从SqlTypes映射到最普通的.NET类型,我认为最好的选择就是将MSDN文档中的映射表转换为代码。请注意,MSDN上的表具有(至少)两个错误:#1:没有称为“ DateTime2”的.NET类型(我使用DateTime),也没有名为“ Xml”的类型(我使用SqlXml)。
无论如何,这是我一直在使用的映射-使用Dictionary而不是使用switch来简化访问,而无需使用单独的方法。
public static Dictionary<SqlDbType, Type> TypeMap = new Dictionary<SqlDbType, Type> { { SqlDbType.BigInt, typeof(Int64) }, { SqlDbType.Binary, typeof(Byte[]) }, { SqlDbType.Bit, typeof(Boolean) }, { SqlDbType.Char, typeof(String) }, { SqlDbType.Date, typeof(DateTime) }, { SqlDbType.DateTime, typeof(DateTime) }, { SqlDbType.DateTime2, typeof(DateTime) }, { SqlDbType.DateTimeOffset, typeof(DateTimeOffset) }, { SqlDbType.Decimal, typeof(Decimal) }, { SqlDbType.Float, typeof(Double) }, { SqlDbType.Int, typeof(Int32) }, { SqlDbType.Money, typeof(Decimal) }, { SqlDbType.NChar, typeof(String) }, { SqlDbType.NText, typeof(String) }, { SqlDbType.NVarChar, typeof(String) }, { SqlDbType.Real, typeof(Single) }, { SqlDbType.SmallInt, typeof(Int16) }, { SqlDbType.SmallMoney, typeof(Decimal) }, { SqlDbType.Structured, typeof(Object) }, // might not be best mapping... { SqlDbType.Text, typeof(String) }, { SqlDbType.Time, typeof(TimeSpan) }, { SqlDbType.Timestamp, typeof(Byte[]) }, { SqlDbType.TinyInt, typeof(Byte) }, { SqlDbType.Udt, typeof(Object) }, // might not be best mapping... { SqlDbType.UniqueIdentifier, typeof(Guid) }, { SqlDbType.VarBinary, typeof(Byte[]) }, { SqlDbType.VarChar, typeof(String) }, { SqlDbType.Variant, typeof(Object) }, { SqlDbType.Xml, typeof(SqlXml) }, };
请注意,您需要注意的一件事是大小/精度- 某些SQL类型(例如varchar)有大小限制,而.NET类型(例如string)没有大小限制。因此,仅仅知道最可能的.NET类型是远远不够的……如果您将其用于例如驱动验证规则,则还需要能够防止用户输入无效的字符(例如,太大的字符)。 )值(例如精度)来了解更多有关参数的信息。请注意,如果您查看SqlClient源代码,它们将使用特殊的代码来处理诸如从相应的SQL精度设置Decimal类型的精度之类的情况。
varchar
string
请注意,如果您需要.NET类型的唯一原因是能够将数据填充到存储的proc参数中,则可能需要尝试对所有.NET值简单地使用ToString(),将字符串填充到SqlParameter,然后查看框架是否将为您进行转换/解析。例如,对于XML或Date参数,您可以代替发送字符串。
另外,由于存在一个已知的(且较小的)类型列表,因此不必使用反射在每个类型上查找Parse()方法,而是可以通过对每个类型使用强类型的解析代码(例如下面的代码)来获得更好的性能。(请注意,几种类型(例如SqlDbType.Udt)不一定具有明显的解析器方法- 您需要弄清楚如何处理这些类型。)
public static Dictionary<SqlDbType, Func<string, object>> TypeMapper = new Dictionary<SqlDbType, Func<string, object>> { { SqlDbType.BigInt, s => Int64.Parse(s)}, { SqlDbType.Binary, s => null }, // TODO: what parser? { SqlDbType.Bit, s => Boolean.Parse(s) }, { SqlDbType.Char, s => s }, { SqlDbType.Date, s => DateTime.Parse(s) }, { SqlDbType.DateTime, s => DateTime.Parse(s) }, { SqlDbType.DateTime2, s => DateTime.Parse(s) }, { SqlDbType.DateTimeOffset, s => DateTimeOffset.Parse(s) }, { SqlDbType.Decimal, s => Decimal.Parse(s) }, { SqlDbType.Float, s => Double.Parse(s) }, { SqlDbType.Int, s => Int32.Parse(s) }, { SqlDbType.Money, s => Decimal.Parse(s) }, { SqlDbType.NChar, s => s }, { SqlDbType.NText, s => s }, { SqlDbType.NVarChar, s => s }, { SqlDbType.Real, s => Single.Parse(s) }, { SqlDbType.SmallInt, s => Int16.Parse(s) }, { SqlDbType.SmallMoney, s => Decimal.Parse(s) }, { SqlDbType.Structured, s => null }, // TODO: what parser? { SqlDbType.Text, s => s }, { SqlDbType.Time, s => TimeSpan.Parse(s) }, { SqlDbType.Timestamp, s => null }, // TODO: what parser? { SqlDbType.TinyInt, s => Byte.Parse(s) }, { SqlDbType.Udt, s => null }, // consider exception instead { SqlDbType.UniqueIdentifier, s => new Guid(s) }, { SqlDbType.VarBinary, s => null }, // TODO: what parser? { SqlDbType.VarChar, s => s }, { SqlDbType.Variant, s => null }, // TODO: what parser? { SqlDbType.Xml, s => s }, };
上面使用的代码非常简单,例如:
string valueToSet = "1234"; SqlParameter p = new SqlParameter(); p.SqlDbType = System.Data.SqlDbType.Int; p.Value = TypeMapper[p.SqlDbType](valueToSet);