2017-09-28 10 views
4

まず、この質問が重複しない理由ここにある:マッピング異なる列挙型を一緒

私は別のenumの変換に関する複数の質問がSOに求められていることを知って、私もone of them自分自身に答えが、すべての質問私は、このトピックでは、名前または値によって異なるenum値を比較するいくつかの方法があることがわかりました。私の特定のケースでは、私は値を知らないし、名前が一致しません。

私は、Ado.Netで作業するときにコードの繰り返しを最小限に抑えるように設計されたADONETHelperというGitHubプロジェクトの一部として、無関係の列挙型の間で値を変換する必要性に直面しました。これは、同じコードをOleDb、Odbc、およびSqlClient(そして今後OracleClientとMySqlClientにもうまくいけばうまくいけばうまくいく)と一緒に使えるようにするためです。

私がしようとしているのは、さまざまな列挙型、特にSQLパラメータデータ型を記述する列挙型の統合を作成することです。 Currntly、私は4つの列挙型サポートしています -
System.Data.DbType
System.Data.SqlClient.SqlDbType
System.Data.OleDb.OleDbType
System.Data.Odbc.OdbcType

を私はOracleClientのかはmysqlclientのサポートを追加したい場合は、私が追加することはかなり難しい作業する必要がありますSystem.Data.OracleClient.OracleTypeまたはMySql.Data.MySqlClient.MySqlDbType。 私はこれをやるよりエレガントな方法を探しています。

だから、最初、私は私自身の列挙型は、ADONETTypeと呼ばれる定義されています。ここ

は私の現在のコード(それは素晴らしい作品が、私は書いたように、それは新しい列挙型のサポートを追加するのは難しい)です。 BooleanByteBinaryCharなどのエントリがあります。 次に、この列挙型に拡張メソッドを提供するために、DBTypeConverterという静的クラスを作成しました。その値は他の列挙型に変換できます。 これは、このクラスは次のようになります。あなたが見ることができるように、OracleClientの、たとえば、のためのサポートを提供するために、私は次のことを行う必要があるでしょう、今

internal static class DBTypeConverter 
{ 
    #region private members 

    private static List<DbTypeMap> _Map; 

    #endregion private members 

    #region static constructor 

    static DBTypeConverter() 
    { 
     _Map = new List<DbTypeMap>() 
     { 
      new DbTypeMap(ADONETType.Boolean, DbType.Boolean, SqlDbType.Bit, OleDbType.Boolean, OdbcType.Bit), 
      new DbTypeMap(ADONETType.Byte, DbType.Byte, SqlDbType.TinyInt, OleDbType.UnsignedTinyInt , OdbcType.TinyInt), 
      new DbTypeMap(ADONETType.Binary, DbType.Binary, SqlDbType.Binary, OleDbType.Binary, OdbcType.Binary), 
      // ... more of the same here ...     
      new DbTypeMap(ADONETType.Xml, DbType.Xml, SqlDbType.Xml, null, null) 
     }; 
    } 

    #endregion static constructor 

    #region methods 

    internal static DbType ToDbType(this ADONETType type) 
    { 
     return type.ConvertTo<DbType>(); 
    } 

    internal static SqlDbType ToSqlType(this ADONETType type) 
    { 
     return type.ConvertTo<SqlDbType>(); 
    } 

    internal static OleDbType ToOleDbType(this ADONETType type) 
    { 
     return type.ConvertTo<OleDbType>(); 
    } 

    internal static OdbcType ToOdbcType(this ADONETType type) 
    { 
     return type.ConvertTo<OdbcType>(); 
    } 

    private static dynamic ConvertTo<T>(this ADONETType type) 
    { 
     var returnValue = _Map.First(m => m.ADONETType == type).GetValueByType(typeof(T)); 
     if(returnValue != null) 
     { 
      return returnValue; 
     } 
     throw new NotSupportedException(string.Format("ADONETType {0} is not supported for {1}", type, typeof(T))); 
    } 

    #endregion methods 

    #region private struct 

    private struct DbTypeMap 
    { 
     #region ctor 

     public DbTypeMap(ADONETType adonetType, DbType? dbType, SqlDbType? sqlDbType, OleDbType? oleDbType, OdbcType? odbcType) 
      : this() 
     { 
      ADONETType = adonetType; 
      DbType = dbType; 
      SqlDbType = sqlDbType; 
      OleDbType = oleDbType; 
      OdbcType = odbcType; 
     } 

     #endregion ctor 

     #region properties 

     internal ADONETType ADONETType { get; private set; } 
     internal DbType? DbType { get; private set; } 
     internal SqlDbType? SqlDbType { get; private set; } 
     internal OleDbType? OleDbType { get; private set; } 
     internal OdbcType? OdbcType { get; private set; } 

     #endregion properties 

     #region methods 

     internal dynamic GetValueByType(Type type) 
     { 
      if (type == typeof(ADONETType)) 
      { 
       return this.ADONETType; 
      } 
      if(type == typeof(DbType)) 
      { 
       return this.DbType; 
      } 
      if (type == typeof(SqlDbType)) 
      { 
       return this.SqlDbType; 
      } 
      if (type == typeof(OleDbType)) 
      { 
       return this.OleDbType; 
      } 
      if (type == typeof(OdbcType)) 
      { 
       return this.OdbcType; 
      } 
      return null; 
     } 

     #endregion methods 
    } 

    #endregion private struct 
} 

  1. 追加DbTypeMap private構造体内のOracleTypeのプロパティです。
  2. DbTypeMapコンストラクタを変更して、oracle型も受け入れるようにします。
  3. 別のケースをGetValueByTypeメソッドのスイッチに追加します。
  4. 静的コンストラクタにoracle型をDBTypeConverterに追加します。
  5. 方法(internal static OracleType ToOracleType(this ADONETType type))をDBTypeConverterに追加します。

これは多くの作業であり、これらの列挙型を統合する別の方法を見つけるよりも、新しいクライアントにサポートを追加する方が簡単です。
これはあなたの熟練者が遊びに来たときです。

+1

しかし、 'DbType'の*のポイント*は異なるシステムで抽象化することです。あなたはクライアントがジェネリッククラス*にコードするのではなく、このマッピングをすべて行う必要があります。 –

+0

列挙型が同じシーケンスにある場合や、リフレクションや設定ファイルなどのアプローチを使用している場合など、唯一の方法は、指定された型の各列挙型を取得し、保持している値もう一度値が同じであれば、ある列挙型から他の列挙型へのキャストも同様です。 – Vnvizitiu

+0

--PS:異なるデータベース用に拡張できる仮想メンバを使用してADONETTypeをカスタムクラスにする方法はありますか? – Vnvizitiu

答えて

3

これは本当に必要と思われる場合(Jeroen's commentと考えてください))等価リストを使って簡単にすることができます。これは単に、配列項目が等しい配列のリストです。クラスの代わりに配列を使用しています。これは、新しい等価を追加するときにプロパティとctor引数を追加する必要がないためです。私は特別なEnumベースクラスを使用している等価を保存するだけでなく、objectもかなりうまく動作します(AFAIK、dynamic用)。 変換を見つける

は(ここでは、コードが賢明なパフォーマンスよりもexplicativeである)だけの問題これらのリスト内の検索の後、次のとおりです。

public static TTo ConvertTo<TTo>(Enum value) 
{ 
    var set = Mapping.FirstOrDefault(values => Array.IndexOf(values, value) != -1); 
    if (set == null) 
     throw new InvalidOperationException($"Value {value} is unknown"); 

    return (TTo)(object)set.First(x => x.GetType() == typeof(TTo)); 
} 

移入Mappingリストに必要な、それは例えば、定義することができて、よう:

private static List<Enum[]> Mapping = new List<Enum[]> 
{ 
    new Enum[] { ADONETType.Byte, DbType.Byte, SqlDbType.TinyInt }, 
    new Enum[] { ADONETType.Boolean, DbType.Boolean, SqlDbType.Bit }, 
    new Enum[] { ADONETType.Binary, DbType.Binary, SqlDbType.Binary }, 
    new Enum[] { ADONETType.Xml, DbType.Xml }, 
}; 

なお、醜いダブルキャスト(TTo)(object) ...えーえ.NETジェネリックは...より良い解決策は大歓迎です。新しい等価性をサポートするには、すべての列挙型の値をこのテーブルにマップする必要がありますが、迷惑ですが1つの場所にローカライズされます。変換が可能でない場合(値valueはどこにも定義されていません)、またはTTo(たとえば、最後のDbType.Xml)への変換が知られていない場合は、InvalidOperationExceptionがスローされます。

+0

ありがとう!これは私のより良い解決策のように思えます。私はそれを行って、あなたに知らせるが、それは私がちょっと他の仕事と結びついているので、しばらく時間がかかるだろう。 –

+0

@ ZoharPeled問題はない、良い一日を! –

関連する問題