.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/ライブラリ/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)];
}
誰かが同じ結果をよりクリーンで、より良く、そして素晴らしい方法で得る方法についての考えを持っていますか?
あなたのアプローチは良い出発点ですが、Ianがコメントで言っているように、その辞書への入力は1回だけ行う必要があります。
同じタイプのセット間で変換されませんが、同じアイデアに基づいたGistがここにあります。 https://Gist.github.com/abrahamjp/858392
警告
以下に 実用的な例 がありますが、このアプローチにはいくつかの問題があることに注意する必要があります。例えば:
string
の場合、Char
、NChar
、VarChar
、NVarChar
、Text
またはNText
(またはXml
、たぶん)の中から正しいものをどのように選びますか?byte[]
などのblobの場合、Binary
、VarBinary
、またはImage
を使用する必要がありますか?decimal
、float
、double
の場合、Decimal
、Float
、Money
、SmallMoney
、Real
のどれを選びますか?DateTime
の場合、DateTime2
、DateTimeOffset
、DateTime
、またはSmallDateTime
が必要ですか?int?
などのNullable
タイプを使用していますか?これらはおそらく、基になる型と同じ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);
この種類のルックアップテーブルは、System.Data
(または.Object
または.Type
)ではなく、System.Webで既に利用できるようです。
プロジェクト->参照の追加-> System.Web-> OK
次に 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;
}
オフィスメイトから、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();
編集:私は考えていましたが、これは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プロパティを保持するインターフェイスがなく、リフレクションは必要ないので、残念です。
完璧ではありませんが、手動で辞書を作成、維持、または入力する必要はありません。既存の辞書で型を検索するだけでよく、型が存在しない場合は、upperメソッドを使用してマッピングを自動的に追加します。ほとんど自動生成されます。
SQL Serverが将来受け取る可能性のある新しいタイプもすべて処理します。