С# DBNull и nullable Types - самая чистая форма преобразования
У меня есть DataTable, который имеет несколько столбцов. Некоторые из этих столбцов имеют значение NULL.
DataTable dt; // Value set.
DataRow dr; // Value set.
// dr["A"] is populated from T-SQL column defined as: int NULL
Итак, самая чистая форма преобразования из значения в DataRow, в переменную с нулевым значением.
В идеале я мог бы сделать что-то вроде:
int? a = dr["A"] as int?;
Изменить. Оказывается, вы МОЖЕТЕ это сделать, побочным эффектом является то, что если ваши типы схем arn't ints, то это ВСЕГДА собирается вернуть null. Ответ Ruben на использование dr.Field<int?>("A")
гарантирует, что несоответствия типов не будут терпеть неудачу. Это, конечно же, будет подхвачено тщательными модульными испытаниями.
Вместо этого я обычно печатаю что-то по строкам:
int? a = dr["A"] != DBNull.Value ? (int)dr["A"] : 0;
Это пучок больше нажатий клавиш, но что более важно, есть больше места для кого-то, чтобы что-то наделать с неправильным нажатием клавиши.
Да, Unit Test выберет это, но я бы предпочел полностью его остановить.
Каков самый чистый, наименее подверженный ошибкам шаблон для этой ситуации.
Ответы
Ответ 1
Хорошо читается глава 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" );
Ответ 2
Это назначение класса DataRowExtensions
в .NET 3.5, который предоставляет статические методы Field<T>
и SetField<T>
для округления значений NULL (и не нулевых) между типами 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), поэтому вы можете использовать тот же механизм для получения и установки полей независимо от их типа данных.
Ответ 3
int? a = (int?)dr["A"]
Ответ 4
Почему бы не использовать LINQ? Он делает конверсию для вас.
Ответ 5
Следующее будет работать безопасно:
Снип:
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);
Ответ 6
Методы расширения!
Что-то вроде следующего:
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");
Ответ 7
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);
Ответ 8
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();