2016-08-31 2 views
1

Ich versuche eine Methode zu erstellen, die eine SQL-Tabelle abfragt und die gefundenen Werte einer neuen Liste von Objekten zuweist. Hier ist ein kleines Beispiel, wie es funktioniert (unter der Annahme, den Leser und die Verbindung eingerichtet sind und einwandfreie Funktion):Inline-Nullprüfung für SqlDataReader-Objekte

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

Es gibt ungefähr 40 andere Eigenschaften auch alle von ihnen auf NULL festlegbare in der MyObject Definition, so dass ich versuche, die Aufträge so sauber wie möglich zu halten. Das Problem ist, dass ich dem Objekt null Werte zuweisen muss, wo immer der Leser eine Null zurückgibt. Im obigen Code, wenn der Leser eine "Data is Null" -Ausnahme auslöst. Ich bin mir bewusst, dass es möglich ist, eine if-Anweisung zu verwenden, um zuerst nach einem DbNull zu suchen, aber da es so viele Eigenschaften gibt, hoffe ich, den Code sauberer zu halten, indem ich eine if Aussage für jede einzelne Eigenschaft buchstabieren muss.

Ein bisschen Suche führte mich zum null-coalescing Operator, der scheint genau das zu tun, was ich will. Also habe ich versucht, die Zuordnungen zu ändern wie folgt aussehen:

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

, die für jede string funktioniert gut, aber gibt mir Fehler von Operator '??' cannot be applied to operands of type 'int' and '<null>' (oder einem anderen Datentyp außer string ich speziell die int (und alles andere rief). als nullable in der Objektdefinition, aber hier ist es mir zu sagen, es kann das nicht tun

die Frage

gibt es eine Möglichkeit nulls in diesem Fall zu behandeln, das kann. (1) Inline geschrieben werden (um separate if Anweisungen für jede Eigenschaft zu vermeiden) und (2) Mit jedem Datentyp arbeiten?

Antwort

3

Null aus einer Datenbank ist nicht "null", es ist DbNull.Value. ?? und ?. Operatoren werden in diesem Fall nicht funktionieren. GetInt32, etc. wird eine Ausnahme auslösen, wenn der Wert in der DB Null ist. Ich habe eine generische Methode und halten Sie es einfach:

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

Wenn Ihre DB Nullable-Ints zum Beispiel hat, kann man nicht diejenigen in einen int lesen kann, wenn man wie auf 0 oder etwas auf dem Standard will. Für nullwertfähige Typen können Sie einfach null oder den Standardwert (T) zurückgeben.

Shannon's Lösung ist sowohl übermäßig kompliziert und wird ein Leistungsproblem sein (viele over the top Reflexion) IMO.

+0

Elegant einfach und funktioniert erstaunlich gut. Hut ab vor dir, Sir. – thanby

-1

Hier ist ein Beispiel, das für Felder funktioniert (kann leicht in Eigenschaften konvertiert werden) und ermöglicht Null-Prüfungen. Es macht das gefürchtete wenn (in einem Schalter), aber es ist ziemlich schnell.

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

Wenn es Ihnen egal ist, in welche Reihenfolge die Dinge kommen, können Sie dies auch parallel/asynchron machen. –

1

Sie können eine Reihe von Erweiterungsmethoden für jeden Standard-GetXXXX schreiben. Diese Erweiterungen erhalten einen zusätzlichen Parameter, der standardmäßig zurückgegeben wird, wenn der Wert des Feldes null ist.

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)) 
    { 
     .... 
    } 
} 

Auf diese Weise können Sie Ihren aktuellen Code verlassen, wie ohne Änderungen oder einfach nur die Felder, die die Null als Rück

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

Sie auch eine Version haben können, wo Sie den Spaltennamen stattdessen passieren muss geändert werden der Ordinalposition und die Suche nach der Position innerhalb der Erweiterung, aber das ist wahrscheinlich nicht gut aus Sicht der Leistung.

Verwandte Themen