public static class DataRecordExtensions
{
public static bool HasColumn(this IDataRecord dr, string columnName)
{
for (int i=0; i < dr.FieldCount; i++)
{
if (dr.GetName(i).Equals(columnName, StringComparison.InvariantCultureIgnoreCase))
return true;
}
return false;
}
}
Использование Exception
s для логики управления, как и в некоторых других ответах, считается плохой практикой и приводит к снижению производительности. Он также отправляет ложные срабатывания профилировщику # сгенерированных исключений, и Бог поможет любому, кто настроит свой отладчик на прерывание сгенерированных исключений.
GetSchemaTable () также является другим предложением во многих ответах. Это не будет предпочтительным способом проверки существования поля, так как он реализован не во всех версиях (он абстрактный и выдает исключение NotSupportedException в некоторых версиях dotnetcore). GetSchemaTable также излишне эффективен, так как это довольно тяжелая функция, если вы посмотрите исходный код .
Циклический просмотр полей может привести к небольшому падению производительности, если вы часто его используете, и вы можете рассмотреть возможность кэширования результатов.
Гораздо лучше использовать эту логическую функцию:Один звонок - без исключений. Это может вызвать исключения внутри, но я так не думаю.ПРИМЕЧАНИЕ: в комментариях ниже мы поняли это ... правильный код на самом деле это:
источник
Я думаю, что вам лучше всего позвонить в GetOrdinal ("columnName") в вашем DataReader и перехватить исключение IndexOutOfRangeException в случае, если столбец отсутствует.
На самом деле, давайте сделаем метод расширения:
редактировать
Хорошо, этот пост начинает набирать несколько голосов в последнее время, и я не могу удалить его, потому что это принятый ответ, поэтому я собираюсь обновить его и (надеюсь) попытаться оправдать использование обработки исключений как контролировать поток.
Другой способ достижения этого, опубликованный Чедом Грантом , состоит в том, чтобы пройтись по каждому полю в DataReader и выполнить сравнение без учета регистра для имени поля, которое вы ищете. Это будет работать очень хорошо, и, честно говоря, вероятно, будет работать лучше, чем мой метод выше. Конечно, я бы никогда не использовал описанный выше метод внутри цикла, где производительность была проблемой.
Я могу вспомнить одну ситуацию, в которой метод try / GetOrdinal / catch будет работать там, где цикл не работает. Однако сейчас это совершенно гипотетическая ситуация, поэтому это очень неубедительное оправдание. В любом случае, терпите меня и посмотрите, что вы думаете.
Представьте себе базу данных, которая позволяет вам «псевдоним» столбцы в таблице. Представьте себе, что я могу определить таблицу со столбцом с именем «EmployeeName», но также дать ей псевдоним «EmpName», и выполнение выбора для любого имени вернет данные в этом столбце. Со мной так далеко?
А теперь представьте, что для этой базы данных есть поставщик ADO.NET, и они для нее кодировали реализацию IDataReader, которая учитывает псевдонимы столбцов.
Теперь
dr.GetName(i)
(как используется в ответе Чада) можно вернуть только одну строку, поэтому он должен возвращать только один из «псевдонимов» в столбце. Тем не мение,GetOrdinal("EmpName")
можно использовать внутреннюю реализацию полей этого провайдера, чтобы проверить псевдоним каждого столбца для искомого имени.В этой гипотетической ситуации с «псевдонимами столбцов» метод try / GetOrdinal / catch будет единственным способом убедиться, что вы проверяете каждую вариацию имени столбца в наборе результатов.
Flimsy? Конечно. Но стоит подумать. Честно говоря, я бы предпочел «официальный» метод HasColumn для IDataRecord.
источник
В одной строке используйте это после поиска DataReader:
Затем,
редактировать
Гораздо эффективнее однострочник, который не требует загрузки схемы:
источник
Вот рабочий образец идеи Жасмин:
источник
это работает для меня:
источник
Следующее просто и работает для меня:
источник
Если вы прочитали вопрос, Майкл спросил о DataReader, а не о DataRecord. Получите ваши объекты правильно.
Используя
r.GetSchemaTable().Columns.Contains(field)
в DataRecord работает, но возвращает столбцы BS (см. Скриншот ниже.)Чтобы увидеть, существует ли столбец данных И содержит ли данные в DataReader, используйте следующие расширения:
Использование:
Вызов
r.GetSchemaTable().Columns
DataReader возвращает столбцы BS:источник
IDataReader
реализуетIDataRecord
. Это разные интерфейсы одного и того же объекта - какICollection<T>
иIEnumerable<T>
разные интерфейсыList<T>
.IDataReader
позволяет перейти к следующей записи, в то время какIDataRecord
позволяет читать из текущей записи. Методы, которые используются в этом ответе, все исходят изIDataRecord
интерфейса. См. Stackoverflow.com/a/1357743/221708 для объяснения того, почему объявление параметраIDataRecord
является предпочтительным.r.GetSchemaTable().Columns
это абсолютно неправильный ответ на этот вопрос.Я написал для пользователей Visual Basic:
Я думаю, что это более мощный и использование:
источник
Вот версия принятого ответа в виде одной строки linq:
источник
Здесь решение от Жасмин в одной строке ... (еще один, простой!):
источник
источник
TLDR:
Множество ответов с претензиями по поводу производительности и плохой практики, поэтому поясняю это здесь.
Маршрут исключения является более быстрым для большего числа возвращаемых столбцов, маршрут петли быстрее для меньшего числа столбцов, а точка пересечения составляет около 11 столбцов. Прокрутите вниз, чтобы увидеть график и тестовый код.
Полный ответ:
Код для некоторых лучших ответов работает, но здесь есть основополагающая дискуссия о «лучшем» ответе, основанном на принятии обработки логики исключений и связанной с этим производительности.
Чтобы уяснить это, я не верю, что есть много рекомендаций относительно исключений CATCHING. У Microsoft есть некоторые рекомендации относительно БРОНИРОВАНИЯ исключений. Там они заявляют:
Первое примечание - снисходительность «если возможно». Что еще более важно, описание дает этот контекст:
Это означает, что если вы пишете API, который может использоваться кем-то другим, дайте ему возможность перемещаться по исключению без попытки / улова. Например, предоставьте TryParse свой метод Parse, генерирующий исключения. Однако нигде это не говорит о том, что вы не должны ловить исключение.
Кроме того, как указывает другой пользователь, уловы всегда допускают фильтрацию по типу и в последнее время позволяют дополнительную фильтрацию с помощью предложения when . Это кажется пустой тратой языковых возможностей, если мы не должны их использовать.
Можно сказать, что за выброшенное исключение есть НЕКОТОРЫЕ затраты, и они МОГУТ влиять на производительность в тяжелой петле. Однако можно также сказать, что стоимость исключения будет незначительной в «связанном приложении». Фактическая стоимость была исследована более десяти лет назад: https://stackoverflow.com/a/891230/852208 Другими словами, стоимость соединения и запроса к базе данных, вероятно, будет меньше, чем стоимость брошенного исключения.
Помимо всего прочего, я хотел определить, какой метод действительно быстрее. Как и следовало ожидать, нет конкретного ответа.
Любой код, который зацикливается на столбцах, становится медленнее, поскольку существует количество столбцов. Можно также сказать, что любой код, использующий исключения, будет работать медленно в зависимости от скорости, с которой не удается найти запрос.
Взяв ответы Чеда Гранта и Мэтта Гамильтона, я запустил оба метода с количеством столбцов до 20 и частотой ошибок до 50% (ОП указал, что он использовал этот два теста между разными процессами, поэтому я предположил, что их всего два) ,
Вот результаты, полученные с помощью LinqPad:
Зигзаги здесь - это частота отказов (столбец не найден) в пределах каждого столбца.
Более узкие результирующие наборы - это хороший выбор. Тем не менее, метод GetOrdinal / Exception не так чувствителен к количеству столбцов и начинает опережать метод зацикливания около 11 столбцов.
Тем не менее, у меня на самом деле нет предпочтений в отношении производительности, поскольку 11 столбцов звучат разумно, так как среднее количество столбцов возвращается во всем приложении. В любом случае мы говорим о долях миллисекунды здесь.
Однако, с точки зрения простоты кода и поддержки псевдонимов, я бы, вероятно, пошел по маршруту GetOrdinal.
Вот тест в форме linqpad. Не стесняйтесь делать репосты собственным методом:
источник
Этот код исправляет проблемы, которые были у Левитикона с их кодом: (адаптировано из: [1]: http://msdn.microsoft.com/en-us/library/system.data.datatablereader.getschematable.aspx )
Причина получения всех этих бесполезных имен столбцов, а не имени столбца из вашей таблицы ... в том, что вы получаете имя столбца схемы (т. Е. Имена столбцов для таблицы схемы)
ПРИМЕЧАНИЕ: похоже, это возвращает только имя первого столбца ...
РЕДАКТИРОВАТЬ: исправленный код, который возвращает имя всех столбцов, но вы не можете использовать SqlDataReader для этого
источник
return r.GetSchemaTable().Rows.Cast<DataRow>().Select(x => (string)x["ColumnName"]).ToList();
:)Чтобы сохранить ваш код надежным и чистым, используйте одну функцию расширения, например:
источник
Я тоже не получил
GetSchemaTable
к работе, пока не нашел этот путь .В основном я делаю это:
источник
Columns.Contains
кстати, нечувствителен к регистру.источник
В вашей конкретной ситуации (все процедуры имеют одинаковые столбцы, кроме 1, который имеет дополнительный 1 столбец), будет лучше и быстрее проверить читателя. Свойство FieldCount, чтобы различать их.
Я знаю, что это старый пост, но я решил ответить, чтобы помочь другим в той же ситуации. Вы также можете (по соображениям производительности) смешать это решение с решением итерации решения.
источник
Мой класс доступа к данным должен иметь обратную совместимость, поэтому я, возможно, пытаюсь получить доступ к столбцу в выпуске, где его еще нет в базе данных. У нас возвращаются довольно большие наборы данных, поэтому я не большой поклонник метода расширения, который должен повторять коллекцию столбцов DataReader для каждого свойства.
У меня есть служебный класс, который создает личный список столбцов, а затем имеет универсальный метод, который пытается разрешить значение на основе имени столбца и типа выходного параметра.
Тогда я могу просто назвать свой код так
источник
Ключ ко всей проблеме здесь :
Если указанные три строки (в настоящее время строки 72, 73 и 74) удалены, то вы можете легко проверить
-1
, чтобы определить, не существует ли столбец.Единственный способ обойти это при обеспечении собственной производительности - это использовать
Reflection
основанную реализацию, например:Usings:
Метод расширения на основе отражения:
источник
Вы также можете вызвать GetSchemaTable () в вашем DataReader, если вам нужен список столбцов, и вы не хотите получать исключение ...
источник
Как насчет
Это, вероятно, не будет столь эффективным в цикле
источник
dr.GetSchemaTable().Columns
содержит в себе - это не то, что вы ищете.Несмотря на то, что нет открытого метода, метод существует во внутреннем классе,
System.Data.ProviderBase.FieldNameLookup
которыйSqlDataReader
опирается.Чтобы получить к нему доступ и получить собственную производительность, вы должны использовать ILGenerator для создания метода во время выполнения. Следующий код даст вам прямой доступ к
int IndexOf(string fieldName)
вSystem.Data.ProviderBase.FieldNameLookup
классе, а также выполнять бухгалтерский учет , чтоSqlDataReader.GetOrdinal()
делает так , что нет никакого побочного эффекта. Сгенерированный код отражает существующее,SqlDataReader.GetOrdinal()
за исключением того, что он вызываетFieldNameLookup.IndexOf()
вместоFieldNameLookup.GetOrdinal()
.GetOrdinal()
Метод вызывает кIndexOf()
функции и генерирует исключение , если-1
возвращается, поэтому мы обойти это поведение.источник
эта работа для меня
источник
Вы можете получить более подробную информацию здесь: Можете ли вы получить имена столбцов из SqlDataReader?
источник