【问题标题】:.NET System Type to SqlDbType.NET 系统类型到 SqlDbType
【发布时间】:2016-06-15 04:31:00
【问题描述】:

我正在寻找 .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)];
     }

有人知道如何以更清洁、更好和更好的方式获得相同的结果吗?

【问题讨论】:

  • 进行字典转换就OK了。一生中完成一次。 :)(少有变化)
  • 如果我的回答对您有帮助,请将其标记为您选择的答案。 :)
  • 抱歉回答太晚了!非常感谢!

标签: c# ado.net system.data


【解决方案1】:

您的方法是一个好的开始,但正如 Ian 在评论中所说,填充该字典应该只完成一次

这里有一个基于相同想法的 GIST,尽管它不会在相同的类型集之间进行转换:https://gist.github.com/abrahamjp/858392

警告

我在下面有a working example,但您需要注意这种方法确实存在一些问题。例如:

  • 对于string,您如何在CharNCharVarCharNVarCharTextNText 之间选择正确的一个(甚至是Xml,也许)
  • 对于像byte[] 这样的blob,您应该使用BinaryVarBinary 还是Image
  • 对于decimalfloatdouble,你应该选择DecimalFloatMoneySmallMoney还是Real
  • 对于DateTime,您需要DateTime2DateTimeOffsetDateTime 还是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);

【讨论】:

  • 看起来不错!我只会添加一个检查以查看字典中是否存在该类型(ContainsKey),如果不使用您自己的详细消息而不是默认的KeyNotFoundException,则抛出NotSupportedException(或自定义异常)。如果传入了不受支持的类型,这可能会使以后的故障排除变得更容易。
  • 感谢您的提示。我已经编辑了答案以抛出ArgumentException,因为NotSupportedException 不适合这种类型的事情。
【解决方案2】:

这种查找表似乎已经可用,尽管不在 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;
}

【讨论】:

  • 谢谢你,这是我需要的完美信息!
【解决方案3】:

我的办公室伙伴给了我尝试 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();

https://dotnetfiddle.net/8heM4H

【讨论】:

  • 请注意,这不处理可为空的类型。
  • 以什么方式?我转换了一些文字(即(int?)4),它似乎工作 - 仍然返回 int 作为 sql 数据类型。请记住,这是在 sql 中获取等效的数据类型。与真正有两种类型的 .net 不同,可空性是数据类型中固有的,然后通过 constraint 被拒绝。
【解决方案4】:

编辑:我在考虑这适用于 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 属性并且不需要反射。
它并不完美,但您不必手动创建、维护或填充字典。您可以在现有字典中查找类型,如果不存在,则使用上层方法自动添加映射。 几乎是自动生成的。
还负责 SQL Server 将来可能收到的任何新类型。

【讨论】:

  • “已编辑:不确定我回复的评论去了哪里。”啊,你是对的,对于另一个方向,我没有比预先填充的字典更好的答案。虽然通常用例是从 sql 类型到 clr 类型,因为一个 sql 类型可以映射到多个 clr 类型。
  • 看起来 SqlDbType 是一个枚举,所以我不确定它如何保存有关 CLR 类型的其他信息。同样对于创建查询,它也不起作用,仅用于将查询结果转换为正确的 CLR 类型。
  • 对不起,我马上删除了我的评论,因为我想重新考虑一下。我最初的评论是“这不是相反的方向吗?”。现在我更喜欢@Igor,因为所需的SqlDbType 是一个枚举。
  • 你说得对,我一直在想 System.Data.SqlTypes 命名空间。 msdn.microsoft.com/en-us/library/…我的错。
猜你喜欢
  • 2011-01-29
  • 1970-01-01
  • 2020-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-11
相关资源
最近更新 更多