2016-03-02 53 views
8

Tôi đang tìm kiếm một chuyển đổi thông minh giữa .Net System.Type và SqlDbType. Những gì tôi thấy đó là ý tưởng sau đây:Loại Hệ thống .NET cho 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; 
    } 

Đoạn mã trên là không thực sự tốt đẹp và là một mùi mã, đó là lý do tôi đã viết như sau, chức năng ngây thơ, không thông minh, nhưng hữu ích, dựa trên 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)]; 
    } 

Có ai có ý tưởng làm sao để có được kết quả tương tự một cách sạch hơn, tốt hơn và tốt đẹp hơn không?

+2

Làm chuyển đổi từ điển là OK. Xong * một lần * trong một thời gian cuộc sống. :) (ít có thay đổi) – Ian

+0

Nếu câu trả lời của tôi đã giúp bạn, hãy đánh dấu nó là câu trả lời bạn đã chọn. :) –

Trả lời

13

Cách tiếp cận của bạn là một khởi đầu tốt, nhưng việc điền từ điển đó chỉ nên được thực hiện một lần, như Ian đã nói trong một nhận xét.

Có một GIST ở đây đó là dựa trên ý tưởng tương tự, mặc dù nó không chuyển đổi giữa các bộ cùng các loại: https://gist.github.com/abrahamjp/858392

Caveat

Tôi có a working example dưới đây, nhưng bạn cần lưu ý rằng cách tiếp cận này có một vài vấn đề. Ví dụ:

  • cho một string, làm thế nào để bạn chọn đúng giữa Char, NChar, VarChar, NVarChar, Text hoặc NText(hoặc thậm chí Xml, có lẽ)?
  • Và đối với các đốm màu như byte[], bạn có nên sử dụng Binary, VarBinary hoặc Image?
  • Đối decimal, floatdouble, bạn nên đi cho Decimal, Float, Money, SmallMoney hoặc Real?
  • Đối với một số DateTime, bạn có cần DateTime2, DateTimeOffset, DateTime hoặc SmallDateTime?
  • Bạn đang sử dụng Nullable các loại, như int?? Những người có nhiều khả năng sẽ cung cấp cùng một loại SqlDbType làm loại cơ bản.

Ngoài ra, chỉ cần cung cấp Type cho bạn biết về các ràng buộc khác, như kích thước và độ chính xác của trường. Đưa ra quyết định đúng cũng là về cách dữ liệu được sử dụng trong ứng dụng của bạn và cách dữ liệu được lưu trữ trong cơ sở dữ liệu.

Điều tốt nhất cần làm là thực sự để cho phép ORM làm điều này cho bạn.

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)); 
    } 
} 

Và đây là cách bạn sẽ sử dụng nó:

var sqlDbType = SqlHelper.GetDbType<string>(); 
// or: 
var sqlDbType = SqlHelper.GetDbType(typeof(DateTime?)); 
// or: 
var sqlDbType = SqlHelper.GetDbType(property.PropertyType); 
+1

Có vẻ tốt! Tôi sẽ chỉ thêm một kiểm tra để xem loại tồn tại ('ContainsKey') trong từ điển và nếu không ném một' NotSupportedException' (hoặc tùy chỉnh ngoại lệ) với thông điệp chi tiết của riêng bạn thay vì mặc định 'KeyNotFoundException'. Điều này có thể làm cho việc khắc phục sự cố dễ dàng hơn sau này nếu loại không được hỗ trợ được chuyển vào. – Igor

+1

Cảm ơn bạn đã tìm mẹo. Tôi đã chỉnh sửa câu trả lời để ném một 'ArgumentException', vì' NotSupportedException' không có nghĩa là cho loại điều này. –

0

Edit: Tôi đã suy nghĩ về và các công trình này cho System.Data.SqlTypes loại.Tôi sẽ để nó ở đây trong trường hợp nó giúp ai đó trong tương lai.

tôi làm điều gì đó như thế này:

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(); 
} 

Bởi vì mỗi SqlDbType có một tài sản .Value đó là cơ bản kiểu CLR thực tế tôi sử dụng phản ánh để có được nó. Nó quá xấu SqlDbType không có một số giao diện mà sẽ giữ này .Value tài sản và phản ánh sẽ không cần thiết.
Nó không hoàn hảo nhưng bạn không phải tự tạo, duy trì hoặc điền từ điển. Bạn chỉ có thể tìm kiếm một loại trong một dict hiện có, và nếu nó không tồn tại, hãy sử dụng phương thức trên để thêm ánh xạ tự động. Khá nhiều tự động tạo.
Cũng đảm nhiệm mọi loại mới mà SQL Server có thể nhận được trong tương lai.

+0

"Đã chỉnh sửa: Không chắc nơi tôi đã trả lời nhận xét." ah, bạn nói đúng, theo một hướng khác tôi không có câu trả lời nào tốt hơn một từ điển được điền trước. Mặc dù thường là trường hợp sử dụng là từ loại sql để loại clr vì một loại sql có thể ánh xạ tới nhiều loại clr. –

+0

Dường như [SqlDbType] (https://msdn.microsoft.com/en-us/library/system.data.sqldbtype (v = vs.110) .aspx) là một kiểu liệt kê vì vậy tôi không chắc chắn cách giữ bổ sung thông tin về Loại CLR. Ngoài ra để tạo truy vấn, nó sẽ không hoạt động, chỉ để dịch kết quả từ truy vấn sang loại CLR chính xác. – Igor

+0

Xin lỗi, tôi đã xóa nhận xét của tôi ngay lập tức vì tôi muốn suy nghĩ lại một chút. Bình luận ban đầu của tôi là * "Điều này không đi theo hướng ngược lại?" *. Bây giờ tôi đang nhiều hơn với @Igor, như mong muốn 'SqlDbType' là một liệt kê. –

0

Dường như loại bảng tra cứu này đã có sẵn, mặc dù không có trong System.Data (hoặc .Object hoặc .Type) mà đúng hơn là trong System.Web.

Project -> Add Reference -> System.Web -> OK

Sau đó https://msdn.microsoft.com/en-us/library/system.data.sqldbtype(v=vs.110).aspx cũng nói

Khi thiết lập các thông số lệnh, các SqlDbType và DbType được liên kết. Do đó, đặt DbType thay đổi SqlDbType thành một hỗ trợ SqlDbType.

Vì vậy, về mặt lý thuyết này nên làm việc;)

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; 
} 
Các vấn đề liên quan