Как обрабатывать нулевые значения в моих бизнес-объектах - PullRequest
1 голос
/ 06 октября 2009

Допустим, у меня есть собственный класс, подобный этому:

public class Customer    
{        
 public int CustomerID { get; set; }        
 public string CompanyName { get; set; }        
 public string BusinessAddress { get; set; }        
 public string Phone { get; set; } 
 public int ParentID { get; set; } 
}

Я создаю пользовательские объекты из базы данных, используя datareader. Пример:

  while (dr.Read())
  {
    listCustomers.Add(new Customer(
          Convert.ToInt32(dr["CustomerID"]),
          Convert.ToString(dr["CompanyName"]),
          Convert.ToString(dr["BusinessAddress"]),
          Convert.ToString(dr["Phone"]),
          Convert.ToInt32(dr["ParentID"]),
)

ParentID может быть нулевым в базе данных (и я не могу его изменить). Когда он нулевой, преобразование явно не удается.

Как мне обращаться с нулевыми значениями, полученными из БД, для заполнения моих бизнес-объектов? Будет ли хорошей практикой использование Nullable Types в моем пользовательском классе? Любые другие советы?

Ответы [ 6 ]

6 голосов
/ 06 октября 2009

Абсолютно. Обнуляемые типы отлично подойдут. В противном случае вам придётся придумать какое-то глупое соглашение а-ля "когда ParentID равен -1, это означает, что Customer не имеет родителя". Обнуляемые типы применяют это «по замыслу»: если нет родителя, ParentID будет null.

Что касается увлажнения ваших объектов, рассмотрите возможность использования инструментов ORM (таких как NHibernate или BLToolkit ), поскольку на самом деле вы не хотите тратить 50% времени на разработку SQL запросы и заполнение ваших объектов из чтения данных

3 голосов
/ 06 октября 2009

Используйте обнуляемые типы в ваших бизнес-объектах. Затем вы можете использовать универсальный вспомогательный метод, подобный этому, чтобы извлечь пустые поля из базы данных.

public static T? GetValue<T>(IDataRecord record, string columnName)
    where T : struct
{
    int columnIndex = record.GetOrdinal(columnName);
    if (record.IsDBNull(columnIndex))
        return null;
    else
        return (T)Convert.ChangeType(record[columnIndex], typeof(T));
}

Так что, если вы ParentId объявлены как int?, при загрузке строки вы сделаете следующее.

obj.ParentId = GetValue<int>(dr, "ParentId");
2 голосов
/ 06 октября 2009

Обнуляемые типы подходят для пустых полей.

Обработка задания с троичным: -

ParentID = ( dr["ParentID"] is DBNull ) ? null : (int)dr["ParentID"];

Кроме того, вам не нужно использовать Convert, если вы знаете, каковы ваши базовые поля БД и их представления .NET.

Я ограничиваю использование функции convert для ситуаций , где я знаю, что типы различаются (скажем, int для строки).

Когда я знаю соответствующий тип .NET, я просто произнесу: -

listCustomers.Add(new Customer(
          (int)dr["CustomerID"],
          (string)dr["CompanyName"],
          (string)dr["BusinessAddress"],
          (string)dr["Phone"],
          ( dr["ParentID"] is DBNull ) ? null : (int)dr["ParentID"]));

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

Рассмотрим следующие примеры: -

public void Populate( int? facilityId, string name, bool? somethingElse )...
public void Populate( string facilityCode, string name, bool? somethingElse )...

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

2 голосов
/ 06 октября 2009

Как насчет добавления таких помощников:

    static string safeGetStringFromDB( IDataReader dr, string strField )
    {
        int nIndex = dr.GetOrdinal( strField );
        if ( dr.IsDBNull( nIndex ) )
            return string.Empty;
        return dr.GetString( nIndex );
    }
1 голос
/ 06 октября 2009

Вы можете создать такие вспомогательные методы:

        static T Map<T>(object obj, Func<object, T> map, T def)
    {
        if (obj != null)
        {
            return map(obj);
        }
        return def;
    }

    static T Map<T>(object obj, Func<object, T> map)
    {
        return Map<T>(obj, map, default(T));
    }

И используйте их как:

            object o = 1;
        var t = Map(o, Convert.ToInt32, 0); // with default value
        var t2 = Map(o, Convert.ToInt32); // or without default value
0 голосов
/ 06 октября 2009

ParentID - это строка в вашем классе, и вы конвертируете ее в Int32.

Но это в стороне: вы знаете базу данных, поэтому вы знаете, какие поля могут быть нулевыми. Я бы также сделал все эти поля обнуляемыми в объекте данных, потому что, на мой взгляд, это лучший способ моделировать базу данных как можно ближе.

...