2016-08-31 8 views
1

SQLテーブルを照会し、見つかった値を新しいオブジェクトリストに割り当てるメソッドを構築しようとしています。ここではそれがどのように動作するかの簡単な例は、(リーダーと接続が設定され、正常に動作していると仮定)です:SqlDataReaderオブジェクトのインラインNullチェック

List<MyObject> results = new List<MyObject>(); 
int oProductID = reader.GetOrdinal("ProductID"); 
int oProductName = reader.GetOrdinal("ProductName"); 

while (reader.Read()) 
{ 
    results.Add(new MyProduct() { 
     ProductID = reader.GetInt32(oProductID), 
     ProductName = reader.GetString(oProductName) 
    }); 
} 

約40他のプロパティはMyObject定義におけるNULL可能なそれらのすべてので、私は、あまりにもあります。割り当てをできるだけ整然としたままにしようとしています。問題は、読者がnullを返すたびに、オブジェクトにnull値を割り当てる必要があることです。上記のコードでは、リーダーが "Data is Null"例外をスローした場合。 ifステートメントを使用してDbNullを最初に確認することは可能ですが、非常に多くのプロパティがあるため、すべてのプロパティに対してifステートメントを記述する必要がないため、コードをきれいに保ちたいと考えています。

検索のビットがnull-coalescingオペレータにつながりました。これは、私が欲しいものを正確に行う必要があるようです。だから私はこのようにして割り当てを変更しようとした:

ProductID = reader.GetInt32(oProductID) ?? null, 
ProductName = reader.GetString(oProductName) ?? null 

任意のstringのため正常に動作しますが、私にstring除くOperator '??' cannot be applied to operands of type 'int' and '<null>'(または他のデータ型のエラーを与える私は、具体的int(および他のすべて)アウトと呼ばれます。ここではオブジェクト定義でNULL可能、しかしとして、それは、それはそれを行うことができない私に言っています

質問

、この場合缶にNULL値を処理するための方法ですが:(1)明確にインラインで書かれています(各物件の個別のifステートメントを避けるため)。(2)どのデータ型でも使用できますか?

答えて

3

データベースからのヌルが「null」でない場合は、DbNull.Valueです。 ??と?この場合、演算子は動作しません。 GetInt32などは、値がDB内でnullの場合は例外をスローします。私は、一般的な方法を行うと、それをシンプルに保つ:

T SafeDBReader<T>(SqlReader reader, string columnName) 
{ 
    object o = reader[columnName]; 

    if (o == DBNull.Value) 
    { 
     // need to decide what behavior you want here 
    } 

    return (T)o; 
} 

あなたのDBは、例えばNULL値可能int型を持っている場合は、0またはのようなものをデフォルトにしたい場合を除き、あなたはint型にそれらを読み取ることができません。 null可能な型の場合、nullまたはデフォルト(T)だけを返すことができます。

シャノンのソリューションは、過度に複雑であり、パフォーマンス上の問題(多くのトップリフレクション)IMOになります。

+0

エレガントにシンプルで、驚くほどうまく機能します。帽子はあなたに捨てられています。 – thanby

-1

次の例は、フィールドで機能し(プロパティに簡単に変換できる)、ヌルチェックが可能な例です。 if(スイッチで)恐ろしいですが、かなり速いです。

public static object[] sql_Reader_To_Type(Type t, SqlDataReader r) 
    { 
     List<object> ret = new List<object>(); 
     while (r.Read()) 
     { 
      FieldInfo[] f = t.GetFields(); 
      object o = Activator.CreateInstance(t); 
      for (int i = 0; i < f.Length; i++) 
      { 
       string thisType = f[i].FieldType.ToString(); 
       switch (thisType) 
       { 
        case "System.String": 

         f[i].SetValue(o, Convert.ToString(r[f[i].Name])); 
         break; 
        case "System.Int16": 
         f[i].SetValue(o, Convert.ToInt16(r[f[i].Name])); 
         break; 
        case "System.Int32": 
         f[i].SetValue(o, Convert.ToInt32(r[f[i].Name])); 
         break; 
        case "System.Int64": 
         f[i].SetValue(o, Convert.ToInt64(r[f[i].Name])); 
         break; 
        case "System.Double": 
         double th; 
         if (r[f[i].Name] == null) 
         { 
          th = 0; 
         } 
         else 
         { 
          if (r[f[i].Name].GetType() == typeof(DBNull)) 
          { 
           th = 0; 
          } 
          else 
          { 
           th = Convert.ToDouble(r[f[i].Name]); 
          } 
         } 
         try { f[i].SetValue(o, th); } 
         catch (Exception e1) 
         { 
          throw new Exception("can't convert " + f[i].Name + " to doube - value =" + th); 
         } 
         break; 
        case "System.Boolean": 
         f[i].SetValue(o, Convert.ToInt32(r[f[i].Name]) == 1 ? true : false); 
         break; 
        case "System.DateTime": 
         f[i].SetValue(o, Convert.ToDateTime(r[f[i].Name])); 
         break; 
        default: 
         throw new Exception("Missed data type in sql select "); 

       } 
      } 
      ret.Add(o); 

     } 
     return ret.ToArray(); 


    } 
+0

何が入ってくるか気にしない場合は、この並列/非同期も使用できます。 –

1

標準のGetXXXXそれぞれに対して、一連の拡張メソッドを記述することができます。これらの拡張は、フィールドの値がnullの場合に返されるデフォルトの追加のパラメーターを受け取ります。

public static class SqlDataReaderExtensions 
{ 
    public int GetInt32(this SqlDataReader reader, int ordinal, int defValue = default(int)) 
    { 
     return (reader.IsDBNull(ordinal) ? defValue : reader.GetInt32(ordinal); 
    } 
    public string GetString(this SqlDataReader reader, int ordinal, int defValue = "") 
    { 
     return (reader.IsDBNull(ordinal) ? defValue : reader.GetString(ordinal); 
    } 
    public int GetDecimal(this SqlDataReader reader, int ordinal, decimal defValue = default(decimal)) 
    { 
     .... 
    } 
} 

これは変更なしであるか、単にまた、あなたは代わりに列名を渡すバージョンを持つことができ、戻り

while (reader.Read()) 
{ 
    results.Add(new MyProduct() { 
     ProductID = reader.GetInt32(oProductID), 
     ProductName = reader.GetString(oProductName, "(No name)"), 
     MinReorder = reader.GetInt32(oReorder, null) 
     ..... 
    }); 
} 

としてnullを必要とするフィールドを変更すると、あなたの現在のコードを残すことができます拡張機能内の位置を検索しますが、これはおそらくパフォーマンスの観点からは良くありません。

関連する問題