C # DBNull и nullable Types - чистейшая форма конвертации - PullRequest
36 голосов
/ 10 ноября 2009

У меня есть DataTable, который имеет ряд столбцов. Некоторые из этих столбцов обнуляются.

DataTable dt;  // Value set. 
DataRow dr;  // Value set. 

// dr["A"] is populated from T-SQL column defined as: int NULL 

Какая самая чистая форма преобразования значения в DataRow в переменную, допускающую значение NULL?

В идеале я бы мог сделать что-то вроде:

int? a = dr["A"] as int?; 

Редактировать : Оказывается, вы МОЖЕТЕ сделать это, побочным эффектом является то, что если ваши типы схем не являются целочисленными, то это ВСЕГДА будет возвращать ноль. Ответ Рубена на использование dr.Field<int?>("A") гарантирует, что несоответствия типов не произойдут без ошибок. Это, конечно, будет подхвачено тщательными юнит-тестами.

Вместо этого я обычно набираю что-то вроде:

int? a = dr["A"] != DBNull.Value ? (int)dr["A"] : 0; 

Это намного больше нажатий клавиш, но что еще более важно, есть больше места для того, чтобы кто-то набил что-то неправильным нажатием клавиши. Да, юнит тест подхватит это, но я бы лучше вообще его остановил.

Какой самый чистый, наименее подверженный ошибкам шаблон для этой ситуации.

Ответы [ 8 ]

54 голосов
/ 10 ноября 2009

Глава LINQ to DataSets из LINQ в действии - хорошее чтение.

Одна вещь, которую вы увидите, это метод расширения Field<T>, который используется следующим образом: -

int? x = dr.Field<int?>( "Field" );

Или

int y = dr.Field<int?>( "Field" ) ?? 0;

Или

var z = dr.Field<int?>( "Field" );
12 голосов
/ 10 ноября 2009

Это цель класса DataRowExtensions в .NET 3.5, который предоставляет статические методы Field<T> и SetField<T> для циклического прерывания обнуляемых (и не обнуляемых) данных между типами DataRow и .NET. .

int? fld = row.Field<int?>("ColumnA")

установит fld в null, если row["ColumnA"] содержит DBNull.Value, в его значение, если оно содержит целое число, и выдаст исключение, если оно содержит что-либо еще. И на обратном пути

row.SetField("ColumnA", fld);

делает то же самое в обратном порядке: если fld содержит null, он устанавливает row["ColumnA"] в DBNull.Value, а в противном случае устанавливает его в значение fld.

Существуют перегрузки Field и SetField для всех типов значений, которые поддерживает DataRow (включая типы, не допускающие значения NULL), поэтому вы можете использовать один и тот же механизм для получения и установки полей независимо от их типа данных.

4 голосов
/ 10 ноября 2009
int? a = (int?)dr["A"]
1 голос
/ 10 ноября 2009

Следовало бы работать, безопасно:

Снип:

public static class SqlDataReaderEx
{
    public static int TryParse(SqlDataReader drReader, string strColumn, int nDefault)
    {
        int nOrdinal = drReader.GetOrdinal(strColumn);
        if (!drReader.IsDbNull(nOrdinal))
            return drReader.GetInt32(nOrdinal);
        else
            return nDefault;
    }
}

Использование:

SqlDataReaderEx.TryParse(drReader, "MyColumnName", -1);
1 голос
/ 10 ноября 2009

Почему бы не использовать LINQ? Это делает преобразование для вас.

0 голосов
/ 27 февраля 2015
public static object GetColumnValue(this DataRow row, string columnName)
{
    if (row.Table.Columns.Contains(columnName))
    {
        if (row[columnName] == DBNull.Value)
        {
            if (row.Table.Columns[columnName].DataType.IsValueType)
            {
                return Activator.CreateInstance(row.Table.Columns[columnName].DataType);
            }
            else
            {
                return null;
            }
        }
        else
        {
            return row[columnName];
        }
    }
    return null;
}

Для вызова функции вы можете написать

var dt = new DataTable();
dt.Columns.Add("ColumnName");
....
Add rows in Datatable.
....
dt.Rows[0].GetColumnValue("ColumnName);
0 голосов
/ 25 сентября 2014
   Chart.data = new List < int ?> ();
   Chart.data = (from DataRow DR in _dtChartData.Rows
    select(int ? )((DR[_ColumnName] == DBNull.Value) ? (int ? ) null : (int ? ) DR[_ColumnName])).ToList();
0 голосов
/ 10 ноября 2009

Методы расширения!

Примерно так:

public static class DataRowExtensions
{
    public static Nullable<T> GetNullableValue<T>(this DataRow row, string columnName)
        where T : struct
    {
        object value = row[columnName];
        if (Convert.IsDBNull(value))
            return null;

        return (Nullable<T>)value;
    }

    public static T GetValue<T>(this DataRow row, string columnName)
        where T : class
    {
        object value = row[columnName];
        if (Convert.IsDBNull(value))
            return null;

        return (T)value;
    }
}

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

int? a = dr.GetNullableValue<int>("A");

или

string b = dr.GetValue<string>("B");
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...