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

not*_*row 12 .net c# sql reflection

我正在SQL程序之上创建'通用'包装器,我可以解析所有必需参数的名称和sqltypes,但有没有办法如何获得它的'底层'.NET类型?

我的目标是做一些事情:

SqlParameter param;
object value;
object correctParam = param.GetNETType().GetMethod("Parse", 
    new Type[] { typeof(string) }).Invoke(value.ToString());
param.Value = correctParam;
Run Code Online (Sandbox Code Playgroud)

GetNETType是我需要的东西.我知道它可以写成param.SqlDbType中的开关,但这是更短的方式,更短的注释代码意味着更低的维护:)

Jus*_*ant 13

不幸的是,据我所知,这个映射没有在.NET Framework中的代码中公开.我之前已经浏览过.NET Framework参考源,发现在.NET代码中有很多很长的每类型切换语句,就像你想要避免的那样,但它们似乎都没有暴露在外面.

如果你真的只想从SqlTypes映射到最可能的.NET类型,我认为你最好的办法是简单地将MSDN文档中的映射表转换为代码.请注意,MSDN上的表有(至少)两个错误:#1:没有名为"DateTime2"的.NET类型(我使用DateTime),也没有名为"Xml"的类型(我使用的是SqlXml).

无论如何,这是我一直在使用的映射 - 使用Dictionary而不是开关,以便于访问而无需单独的方法.

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) }, 
};
Run Code Online (Sandbox Code Playgroud)

请注意,您需要注意的一件事是大小/精度 - 某些SQL类型(例如varchar)具有大小限制,而.NET类型(例如string)则没有.因此,能够了解最可能的.NET类型并不足够......如果您正在使用它,例如,驱动器验证规则,您还需要能够阻止用户输入无效(例如,太大) )通过了解更多有关参数的值,如精度.请注意,如果查看SqlClient源代码,它们会使用特殊代码来处理从相应的SQL精度设置Decimal类型的精度等情况.

请注意,如果您需要.NET类型的唯一原因是能够将数据填充到存储的proc参数中,您可能想要尝试在所有.NET值上使用ToString(),将字符串填充到Value属性中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 }, 
};
Run Code Online (Sandbox Code Playgroud)

上面使用的代码非常简单,例如:

        string valueToSet = "1234";
        SqlParameter p = new SqlParameter();
        p.SqlDbType = System.Data.SqlDbType.Int;
        p.Value = TypeMapper[p.SqlDbType](valueToSet);
Run Code Online (Sandbox Code Playgroud)