Самый эффективный способ проверить на DBNull и затем присвоить переменной?

150
задан 11 revs, 3 users 84% 23 May 2017 в 12:34
поделиться

12 ответов

Я сделал что-то похожее с дополнительными методами. Вот мой код:

public static class DataExtensions
{
    /// <summary>
    /// Gets the value.
    /// </summary>
    /// <typeparam name="T">The type of the data stored in the record</typeparam>
    /// <param name="record">The record.</param>
    /// <param name="columnName">Name of the column.</param>
    /// <returns></returns>
    public static T GetColumnValue<T>(this IDataRecord record, string columnName)
    {
        return GetColumnValue<T>(record, columnName, default(T));
    }

    /// <summary>
    /// Gets the value.
    /// </summary>
    /// <typeparam name="T">The type of the data stored in the record</typeparam>
    /// <param name="record">The record.</param>
    /// <param name="columnName">Name of the column.</param>
    /// <param name="defaultValue">The value to return if the column contains a <value>DBNull.Value</value> value.</param>
    /// <returns></returns>
    public static T GetColumnValue<T>(this IDataRecord record, string columnName, T defaultValue)
    {
        object value = record[columnName];
        if (value == null || value == DBNull.Value)
        {
            return defaultValue;
        }
        else
        {
            return (T)value;
        }
    }
}

Для использования его Вы сделали бы что-то как

int number = record.GetColumnValue<int>("Number",0)
4
ответ дан 2 revs, 2 users 96% 23 November 2019 в 22:20
поделиться

Это - то, как я обрабатываю чтение из примера DataRows

///<summary>
/// Handles operations for Enumerations
///</summary>
public static class DataRowUserExtensions
{
    /// <summary>
    /// Gets the specified data row.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="dataRow">The data row.</param>
    /// <param name="key">The key.</param>
    /// <returns></returns>
    public static T Get<T>(this DataRow dataRow, string key)
    {
        return (T) ChangeTypeTo<T>(dataRow[key]);
    }

    private static object ChangeTypeTo<T>(this object value)
    {
        Type underlyingType = typeof (T);
        if (underlyingType == null)
            throw new ArgumentNullException("value");

        if (underlyingType.IsGenericType && underlyingType.GetGenericTypeDefinition().Equals(typeof (Nullable<>)))
        {
            if (value == null)
                return null;
            var converter = new NullableConverter(underlyingType);
            underlyingType = converter.UnderlyingType;
        }

        // Try changing to Guid  
        if (underlyingType == typeof (Guid))
        {
            try
            {
                return new Guid(value.ToString());
            }
            catch

            {
                return null;
            }
        }
        return Convert.ChangeType(value, underlyingType);
    }
}

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

if (dbRow.Get<int>("Type") == 1)
{
    newNode = new TreeViewNode
                  {
                      ToolTip = dbRow.Get<string>("Name"),
                      Text = (dbRow.Get<string>("Name").Length > 25 ? dbRow.Get<string>("Name").Substring(0, 25) + "..." : dbRow.Get<string>("Name")),
                      ImageUrl = "file.gif",
                      ID = dbRow.Get<string>("ReportPath"),
                      Value = dbRow.Get<string>("ReportDescription").Replace("'", "\'"),
                      NavigateUrl = ("?ReportType=" + dbRow.Get<string>("ReportPath"))
                  };
}

Опоры к Монстры Получили Мой.Net для кода ChageTypeTo.

4
ответ дан Chris Marisic 23 November 2019 в 22:20
поделиться

Я всегда использую:

if (row["value"] != DBNull.Value)
  someObject.Member = row["value"];

Найденный им короткий и всесторонний.

4
ответ дан Patrick Desjardins 23 November 2019 в 22:20
поделиться

Я стараюсь избегать этой проверки как можно больше.

, Очевидно, не должен быть сделан для столбцов, которые не могут содержать null.

, Если Вы храните в типе значения Nullable (int?, и т.д.), можно просто преобразовать использование as int?.

, Если Вы не должны дифференцироваться между string.Empty и null, можно просто звонить .ToString(), так как DBNull возвратится string.Empty.

5
ответ дан bdukes 23 November 2019 в 22:20
поделиться

Я лично одобряю этот синтаксис, который использует явный метод IsDbNull, представленный IDataRecord, и кэширует индекс столбца для предотвращения дублирующегося строкового поиска.

Расширенный для удобочитаемости, это идет что-то как:

int columnIndex = row.GetOrdinal("Foo");
string foo; // the variable we're assigning based on the column value.
if (row.IsDBNull(columnIndex)) {
  foo = String.Empty; // or whatever
} else { 
  foo = row.GetString(columnIndex);
}

Переписанный для установки на одной строке для компактности в коде DAL - отмечают, что в этом примере мы присваиваемся int bar = -1, если row["Bar"] является пустым.

int i; // can be reused for every field.
string foo  = (row.IsDBNull(i  = row.GetOrdinal("Foo")) ? null : row.GetString(i));
int bar = (row.IsDbNull(i = row.GetOrdinal("Bar")) ? -1 : row.GetInt32(i));

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

6
ответ дан bdukes 23 November 2019 в 22:20
поделиться

Компилятор не оптимизирует далеко индексатор (т.е. если Вы используете строку ["значение"] дважды), таким образом, да это немного более быстро, чтобы сделать:

object value = row["value"];

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

DBNull.Value на самом деле одиночный элемент, так для добавления 4-й опции - Вы могли, возможно, использовать ReferenceEquals - но в действительности, я думаю, что Вы волнуетесь слишком много здесь... Я не думаю, что скорость, отличающаяся между "", "==" и т.д. будет причиной любой проблемы производительности, которую Вы видите. Профиль Ваш весь код и внимание на что-то, что имеет значение..., что не будет это.

20
ответ дан Marc Gravell 23 November 2019 в 22:20
поделиться

Необходимо использовать метод:

Convert.IsDBNull()

Рассмотрение это встроено к Платформе, я ожидал бы, что это будет самым эффективным.

я предложил бы что-то вроде:

int? myValue = (Convert.IsDBNull(row["column"]) ? null : (int?) Convert.ToInt32(row["column"]));

И да, компилятор должен кэшировать его для Вас.

34
ответ дан Jon Grant 23 November 2019 в 22:20
поделиться

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

Т.е.

public static IsDBNull<T>(this object value, T default)
{
    return (value == DBNull.Value)
        ? default
        : (T)value;
}

public static IsDBNull<T>(this object value)
{
    return value.IsDBNull(default(T));
}

Тогда:

IDataRecord record; // Comes from somewhere

entity.StringProperty = record["StringProperty"].IsDBNull<string>(null);
entity.Int32Property = record["Int32Property"].IsDBNull<int>(50);

entity.NoDefaultString = record["NoDefaultString"].IsDBNull<string>();
entity.NoDefaultInt = record["NoDefaultInt"].IsDBNull<int>();

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

Просто мысль.

5
ответ дан Richard Szalay 23 November 2019 в 22:20
поделиться

Я бы использовал следующий код на C # ( VB.NET не так прост).

Код присваивает значение, если оно не равно нулю / DBNull, в противном случае он присваивает значение по умолчанию, которое может быть установлено на значение LHS, позволяя компилятору игнорировать присвоение.

oSomeObject.IntMemeber = oRow["Value"] as int? ?? iDefault;
oSomeObject.StringMember = oRow["Name"] as string ?? sDefault;
9
ответ дан 3 revs, 2 users 62% 23 November 2019 в 22:20
поделиться

У меня есть IsDBNull в программе, которая считывает большой объем данных из базы данных. С IsDBNull он загружает данные примерно за 20 секунд. Без IsDBNull около 1 секунды.

Поэтому я думаю, что лучше использовать:

public String TryGetString(SqlDataReader sqlReader, int row)
{
    String res = "";
    try
    {
        res = sqlReader.GetString(row);
    }
    catch (Exception)
    { 
    }
    return res;
}
3
ответ дан 23 November 2019 в 22:20
поделиться

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

    static void Main(string[] args)
    {
        object number = DBNull.Value;

        int newNumber = number.SafeDBNull<int>();

        Console.WriteLine(newNumber);
    }



    public static T SafeDBNull<T>(this object value, T defaultValue) 
    {
        if (value == null)
            return default(T);

        if (value is string)
            return (T) Convert.ChangeType(value, typeof(T));

        return (value == DBNull.Value) ? defaultValue : (T)value;
    } 

    public static T SafeDBNull<T>(this object value) 
    { 
        return value.SafeDBNull(default(T)); 
    } 
7
ответ дан 23 November 2019 в 22:20
поделиться

Должно быть, я что-то упускаю. Разве не проверяет DBNull именно то, что делает метод DataRow.IsNull ?

Я использовал следующие два метода расширения:

public static T? GetValue<T>(this DataRow row, string columnName) where T : struct
{
    if (row.IsNull(columnName))
        return null;

    return row[columnName] as T?;
}

public static string GetText(this DataRow row, string columnName)
{
    if (row.IsNull(columnName))
        return string.Empty;

    return row[columnName] as string ?? string.Empty;
}

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

int? id = row.GetValue<int>("Id");
string name = row.GetText("Name");
double? price = row.GetValue<double>("Price");

Если вам не нужны возвращаемые значения Nullable для GetValue , вместо этого вы можете легко вернуть default (T) или какой-либо другой параметр.


Кстати, вот альтернатива VB.NET предложению Stevo3000:

oSomeObject.IntMember = If(TryConvert(Of Integer)(oRow("Value")), iDefault)
oSomeObject.StringMember = If(TryCast(oRow("Name"), String), sDefault)

Function TryConvert(Of T As Structure)(ByVal obj As Object) As T?
    If TypeOf obj Is T Then
        Return New T?(DirectCast(obj, T))
    Else
        Return Nothing
    End If
End Function
71
ответ дан 23 November 2019 в 22:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: