2016-08-31 2 views
1

Я пытаюсь создать метод, который запрашивает таблицу SQL и присваивает значения, которые он находит, в новый список объектов. Вот краткий пример того, как это работает (предположит, что читатель и соединение установлены и работают правильно):Inline null check для объектов SqlDataReader

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 определении, так что я Я стараюсь, чтобы задания были как можно более аккуратными. Проблема в том, что мне нужно назначить нулевые значения объекту, где читатель возвращает нуль. В приведенном выше коде, если читатель выбрасывает исключение «Данные является нулевым». Я знаю, что можно использовать заявление if, чтобы сначала проверить DbNull, но поскольку существует так много свойств, я надеюсь сохранить код чистым, не имея необходимости указывать оператор if для каждого отдельного объекта.

Немного поиска привело меня к оператору null-coalescing, который, похоже, должен делать именно то, что я хочу. Поэтому я попытался изменить задание, чтобы выглядеть следующим образом:

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

Который работает отлично подходит для любого string, но дает мне ошибку Operator '??' cannot be applied to operands of type 'int' and '<null>' (или любому другому типа данных, за исключением string я специально крикнул int (и все остальное). а Nullable в определении объекта, но здесь он говорит мне, что не может сделать это

Вопрос в

есть ли способ справиться с аннулирует в этом случае можно:. (1) Должно быть написано четко в строке (во избежание отдельных операторов для каждой собственности) и (2) Работайте с любыми типами данных?

ответ

3

Null из базы данных не является «нулевым», это DbNull.Value. ?? а также ?. операторы в этом случае работать не будут. GetInt32 и т. Д. Будут генерировать исключение, если в базе данных значение 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 имеет NULLABLE Интс, например, вы не можете прочитать те, в межд, если вы не хотите, чтобы по умолчанию 0 или что-то подобное. Для типов с нулевым значением вы можете просто вернуть значение null или по умолчанию (T).

Решение Шеннона слишком сложное и будет проблемой с производительностью (много над верхним отражением) ИМО.

+0

Элегантно простой и работает удивительно хорошо. Шляпы к тебе, сэр. – thanby

-1

Вот пример, который работает для полей (может быть легко преобразован в свойства) и допускает нулевые проверки. Это ужасно, если (в переключателе), но это довольно быстро.

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

Вы также можете иметь версию, где вы передаете имя столбца вместо ординального положения и выполнять поиск позиции внутри расширения, но это, вероятно, не очень хорошо с точки зрения производительности.