web-dev-qa-db-ja.com

.NETシステムタイプからSqlDbTypeへ

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

誰かが同じ結果をよりクリーンで、より良く、そして素晴らしい方法で得る方法についての考えを持っていますか?

14
Simone Salvo

あなたのアプローチは良い出発点ですが、Ianがコメントで言っているように、その辞書への入力は1回だけ行う必要があります。

同じタイプのセット間で変換されませんが、同じアイデアに基づいたGistがここにあります。 https://Gist.github.com/abrahamjp/858392

警告

以下に 実用的な例 がありますが、このアプローチにはいくつかの問題があることに注意する必要があります。例えば:

  • stringの場合、CharNCharVarCharNVarCharTextまたはNText(またはXml、たぶん)の中から正しいものをどのように選びますか?
  • byte[]などのblobの場合、BinaryVarBinary、またはImageを使用する必要がありますか?
  • decimalfloatdoubleの場合、DecimalFloatMoneySmallMoneyRealのどれを選びますか?
  • DateTimeの場合、DateTime2DateTimeOffsetDateTime、または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);
18
Anders Tornblad

この種類のルックアップテーブルは、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;
}
5
mpag

オフィスメイトから、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

0
b_levitt

編集:私は考えていましたが、これは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が将来受け取る可能性のある新しいタイプもすべて処理します。

0
Mladen Prajdic