Этот вопрос иногда возникает, но я не нашел удовлетворительного ответа.
Типичный шаблон (строка является DataRow ):
if (row["value"] != DBNull.Value)
{
someObject.Member = row["value"];
}
Мой первый вопрос, который является более эффективным (я перевернул условие):
row["value"] == DBNull.Value; // Or
row["value"] is DBNull; // Or
row["value"].GetType() == typeof(DBNull) // Or... any suggestions?
Это указывает на то, что .GetType () должен быть быстрее, но, возможно, компилятор знает несколько хитростей, которые я не знаю?
Второй вопрос: стоит ли кэшировать значение row ["value"] или компилятор все равно оптимизирует индексатор?
Например:
object valueHolder;
if (DBNull.Value == (valueHolder = row["value"])) {}
Ноты:
- строка ["значение"] существует.
- Я не знаю индекс столбца столбца (следовательно, поиск имени столбца).
- Я спрашиваю конкретно о проверке DBNull, а затем о назначении (не о преждевременной оптимизации и т. Д.).
Я протестировал несколько сценариев (время в секундах, 10 000 000 испытаний):
row["value"] == DBNull.Value: 00:00:01.5478995
row["value"] is DBNull: 00:00:01.6306578
row["value"].GetType() == typeof(DBNull): 00:00:02.0138757
Object.ReferenceEquals имеет ту же производительность, что и "=="
Самый интересный результат? Если вы не соответствуете имени столбца в каждом конкретном случае (например, «Значение» вместо «значение», это займет примерно в десять раз больше (для строки):
row["Value"] == DBNull.Value: 00:00:12.2792374
Мораль этой истории заключается в том, что если вы не можете найти столбец по его индексу, убедитесь, что имя столбца, которое вы передаете в индексатор, точно совпадает с именем DataColumn.
Кэширование значения также кажется почти в два раза быстрее:
No Caching: 00:00:03.0996622
With Caching: 00:00:01.5659920
Таким образом, наиболее эффективный метод выглядит так :
object temp;
string variable;
if (DBNull.Value != (temp = row["value"]))
{
variable = temp.ToString();
}
IDataRecord
расширения.Ответы:
Я должен что-то упустить. Не проверяет
DBNull
точно, чтоDataRow.IsNull
делает метод?Я использовал следующие два метода расширения:
Использование:
Если вы не хотите
Nullable<T>
возвращать значения дляGetValue<T>
, вы можете легко вернутьdefault(T)
или другой вариант вместо этого.На несвязанном примечании вот альтернатива VB.NET предложению Stevo3000:
источник
row.IsNull(columnName)
вы пишете, вы уже читаете это и читаете снова. Не говоря уже о том, что это будет иметь значение, но теоретически это может быть менее эффективно ..System.Data.DataSetExtensions.DataRowExtensions.Field<T>(this System.Data.DataRow, string)
то же самое, что первый метод?Вы должны использовать метод:
Учитывая, что он встроен в Framework, я ожидаю, что он будет наиболее эффективным.
Я бы предложил что-то вроде:
И да, компилятор должен кешировать его для вас.
источник
Компилятор не оптимизирует индексатор (т. Е. Если вы дважды используете row ["value"]), так что да, это сделать немного быстрее:
и затем используйте значение дважды; использование .GetType () может привести к возникновению проблем, если оно не определено ...
DBNull.Value
на самом деле одноэлементный, так что для добавления 4-го варианта - возможно, вы могли бы использовать ReferenceEquals - но на самом деле, я думаю, что вы слишком беспокоитесь здесь ... Я не думаю, что скорость отличается между "есть", "== "и т. д. будет причиной любой проблемы с производительностью, которую вы видите. Профилируйте весь свой код и сосредоточьтесь на чем-то важном ... это не будет так.источник
Я хотел бы использовать следующий код в C # ( VB.NET не так просто).
Код присваивает значение, если оно не равно нулю / DBNull, в противном случае он присваивает значение по умолчанию, которое может быть установлено на значение LHS, позволяя компилятору игнорировать присвоение.
источник
oSomeObject.IntMember = If(TryCast(oRow("Value), Integer?), iDefault)
.TryCast
он не предоставляет такую же удобную функциональность, какas
оператор C # дляNullable(Of T)
типов. Самый близкий способ, которым я могу придумать, чтобы подражать этому, - написать свою собственную функцию, как я сейчас предложил в своем ответе.Я чувствую, что только очень немногие подходы здесь не рискуют больше всего беспокоить потенциального оператора (Марк Гравелл, Stevo3000, Ричард Сзалай, Нил, Даррен Коппанд), и большинство из них излишне сложны. Полностью осознавая, что это бесполезная микрооптимизация, позвольте мне сказать, что вы должны в основном использовать эти:
1) Не считывайте значение из DataReader / DataRow дважды - так что либо кэшируйте его перед проверкой на нуль и приведением / преобразованием, либо, что еще лучше, напрямую передайте
record[X]
объект в пользовательский метод расширения с соответствующей подписью.2) Чтобы выполнить вышесказанное, не используйте встроенную
IsDBNull
функцию в вашем DataReader / DataRow, так как это вызываетrecord[X]
внутренне, так что в действительности вы будете делать это дважды.3) Как правило, сравнение типов всегда будет медленнее сравнения значений. Просто сделай
record[X] == DBNull.Value
лучше.4) Прямое приведение будет быстрее, чем вызов
Convert
класса для преобразования, хотя, боюсь, последнее будет меньше колебаться.5) Наконец, доступ к записи по индексу, а не по имени столбца будет снова быстрее.
Я чувствую, что с подходами Сзалая, Нила и Даррена Коппанда будет лучше. Мне особенно нравится подход метода расширения Даррена Коппанда, который принимает
IDataRecord
(хотя я хотел бы сузить это далееIDataReader
) и имя индекса / столбца.Будьте осторожны, чтобы назвать это:
и нет
в случае, если вам нужно различать
0
иDBNull
. Например, если у вас есть нулевые значения в полях перечисления, в противном случае существуетdefault(MyEnum)
риск возврата первого значения перечисления. Так что лучше позвониrecord.GetColumnValue<MyEnum?>("Field")
.Так как вы чтение из
DataRow
, я хотел бы создать метод расширения для обоегоDataRow
иIDataReader
от сушильного общего кода.Так что теперь назовите это как:
Я считаю, что именно так и должно быть во фреймворке (вместо методов и т. Д.)
record.GetInt32
,record.GetString
Во-первых, без исключений во время выполнения, что дает нам возможность обрабатывать нулевые значения.Из моего опыта мне не повезло с одним общим методом для чтения из базы данных. Я всегда был обычай ручки различных типов, так что мне пришлось писать свои собственные
GetInt
,GetEnum
,GetGuid
и т.д. методы в долгосрочной перспективе. Что, если вы хотите обрезать пробелы при чтении строки из БД по умолчанию или рассматриватьDBNull
как пустую строку? Или, если ваша десятичная дробь должна быть усечена из всех конечных нулей. У меня было больше всего проблем сGuid
типом, где разные драйверы коннектора вели себя по-разному, когда базовые базы данных могли хранить их как строковые или двоичные. У меня перегрузка такая:С подходом Stevo3000 я нахожу вызов немного уродливым и утомительным, и сделать из него общую функцию будет сложнее.
источник
Есть неприятный случай, когда объект может быть строкой. Приведенный ниже код метода расширения обрабатывает все случаи. Вот как вы бы это использовали:
источник
Я лично предпочитаю этот синтаксис, который использует явный метод IsDbNull, представленный
IDataRecord
, и кэширует индекс столбца, чтобы избежать поиска дублирующихся строк.Расширена для удобства чтения, это выглядит примерно так:
Переписать, чтобы поместить в одну строку для компактности в коде DAL - обратите внимание, что в этом примере мы присваиваем значение
int bar = -1
ifrow["Bar"]
, равное нулю.Встроенное присваивание может сбивать с толку, если вы не знаете, что оно там, но оно сохраняет всю операцию в одной строке, что, я думаю, повышает удобочитаемость, когда вы заполняете свойства из нескольких столбцов в одном блоке кода.
источник
Не то чтобы я это сделал, но вы можете обойти вызов двойного индексатора и при этом сохранить свой код в чистоте, используя метод static / extension.
То есть.
Затем:
Также имеет преимущество хранения логики проверки нуля в одном месте. Недостатком является, конечно, то, что это дополнительный вызов метода.
Просто мысль.
источник
Я стараюсь избегать этой проверки в максимально возможной степени.
Очевидно, не нужно делать для столбцов, которые не могут содержать
null
.Если вы храните в типе значений Nullable (
int?
и т. Д.), Вы можете просто конвертировать, используяas int?
.Если вам не нужно различать
string.Empty
иnull
, вы можете просто позвонить.ToString()
, так как DBNull вернетсяstring.Empty
.источник
Я всегда использую:
Нашел это кратко и всесторонне.
источник
Вот как я справляюсь с чтением из DataRows
Пример использования:
Реквизит для монстров получил мой. Net для кода ChageTypeTo.
источник
Я сделал нечто подобное с методами расширения. Вот мой код:
Чтобы использовать это, вы бы сделали что-то вроде
источник
если в DataRow строка ["fieldname"] isDbNull заменит ее на 0, в противном случае получим десятичное значение:
источник
использовать как это
источник
У меня есть IsDBNull в программе, которая читает много данных из базы данных. С IsDBNull он загружает данные примерно за 20 секунд. Без IsDBNull около 1 секунды.
Поэтому я думаю, что лучше использовать:
источник