一尘不染

如何在SQL中检索给定StoredProcedure参数的.NET类型?

sql

我在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内的开关,但这是一种较短的方法,而较短的注释代码意味着较低的维护:)


阅读 132

收藏
2021-03-17

共1个答案

一尘不染

不幸的是,据我所知,.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类型的精度之类的情况。

请注意,如果您需要.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);
2021-03-17