2016-03-02 90 views
8

我正在尋找.Net System.Type和SqlDbType之間的智能轉換。我發現它是以下想法:NET系統類型爲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)]; 
    } 

有人有想法如何以更清潔,更好,更好的方式獲得相同的結果嗎?

+2

製作字典轉換就可以了。在一生中完成*一次*。 :)(少有變化) – Ian

+0

如果我的回答對您有幫助,請將其標記爲您選擇的答案。 :) –

回答

13

你的方法是一個好的開始,但是填充該字典應該只做一次,正如Ian在評論中所說。

這裏有一個GIST是基於同樣的想法,雖然它不一樣套類型之間轉換:https://gist.github.com/abrahamjp/858392

買者

我有以下a working example,但你需要要知道這種方法確實存在一些問題。例如:

  • 對於string,你怎麼挑CharNCharVarCharNVarCharTextNText之間正確的(甚至Xml,也許)
  • 而對於像byte[]這樣的斑點,您是否應該使用Binary,VarBinaryImage
  • 對於decimalfloatdouble,你應該去爲DecimalFloatMoneySmallMoneyReal
  • 對於DateTime,您是否需要DateTime2,DateTimeOffset,DateTimeSmallDateTime
  • 您是否在使用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); 
+1

看起來不錯!我只會添加一個檢查來查看字典中是否存在該類型('ContainsKey'),如果不是使用您自己的詳細消息而不是默認的'KeyNotFoundException'拋出'NotSupportedException'(或自定義異常)。如果傳入不支持的類型,這可能會在以後更輕鬆地排除故障。 – Igor

+1

感謝您的提示。我編輯了答案來拋出'ArgumentException',因爲'NotSupportedException'不適用於這種類型的東西。 –

0

編輯:我在想,這適用於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未來可能收到的任何新類型。

+0

「編輯:不確定我回復的評論去了哪裏。」啊,你是對的,對於另一個方向,我沒有比預先填好的字典更好的答案。雖然通常用例從sql類型到clr類型,因爲一個sql類型可以映射到多個clr類型。 –

+0

看起來像[SqlDbType](https://msdn.microsoft.com/en-us/library/system.data.sqldbtype(v = vs.110).aspx)是一個枚舉,所以我不知道如何持有額外的有關CLR類型的信息。此外,爲了創建查詢,它不起作用,僅用於將查詢結果轉換爲正確的CLR類型。 – Igor

+0

對不起,我立即刪除了我的評論,因爲我想重新考慮一下。我最初的評論是*「這不是相反的方向嗎?」*。現在我更喜歡@Igor,因爲所需的'SqlDbType'是一個枚舉。 –

0

似乎這種查找表已經可用,儘管不在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; 
}