C# DBNull и nullable Types - чистейшая форма конвертации

У меня есть 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 ответов

Решение

Раздел 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" );

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

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

установит fld в null если row["ColumnA"] содержит DBNull.Value, to its value if it contains an integer, and throw an exception if it contains anything else. And on the way back,

row.SetField("ColumnA", fld);

does the same thing in reverse: if fld содержит null, it sets row["ColumnA"] в DBNull.Value, and otherwise sets it to the value of fld,

There are overloads of Field а также SetField for all of the value types that DataRow supports (including non-nullable types), so you can use the same mechanism for getting and setting fields irrespective their data type.

int? a = (int?)dr["A"]

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

Следующее будет работать, безопасно:

Надрез:

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

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

Что-то вроде следующего:

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");
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);
   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();
Другие вопросы по тегам