Итак, я использую IDataReader для гидратации некоторых бизнес-объектов, но во время выполнения я не знаю, какие именно поля будут в считывателе. Любые поля, которых нет в средстве чтения, будут оставлены нулевыми в результирующем объекте. Как вы проверяете, содержит ли читатель определенное поле, не просто заключив его в команду try / catch?





Вы не можете просто проверить читатель ["field"] на null или DBNull, потому что генерируется исключение IndexOutOfRangeException, если столбец отсутствует в читателе.
Код, который я использую в своем слое отображения для создания объектов домена и хранимых процедур, которые используют слой сопоставления, может иметь разные имена столбцов, приведен ниже; вы можете изменить его, чтобы не создавать исключение, если столбец не найден, и возвращать значение по умолчанию (t) или null.
Я понимаю, что это не самое элегантное или оптимальное решение (и действительно, если вы можете этого избежать, то и должны), однако устаревшие хранимые процедуры или запросы Sql могут потребовать обходного пути.
/// <summary>
/// Grabs the value from a specific datareader for a list of column names.
/// </summary>
/// <typeparam name = "T">Type of the value.</typeparam>
/// <param name = "reader">Reader to grab data off of.</param>
/// <param name = "columnNames">Column names that should be interrogated.</param>
/// <returns>Value from the first correct column name or an exception if none of the columns exist.</returns>
public static T GetColumnValue<T>(IDataReader reader, params string[] columnNames)
{
bool foundValue = false;
T value = default(T);
IndexOutOfRangeException lastException = null;
foreach (string columnName in columnNames)
{
try
{
int ordinal = reader.GetOrdinal(columnName);
value = (T)reader.GetValue(ordinal);
foundValue = true;
}
catch (IndexOutOfRangeException ex)
{
lastException = ex;
}
}
if (!foundValue)
{
string message = string.Format("Column(s) {0} could not be not found.",
string.Join(", ", columnNames));
throw new IndexOutOfRangeException(message, lastException);
}
return value;
}
Хотя я не согласен с этим подходом (я думаю, что при доступе к данным вы должны знать форму заранее), я понимаю, что есть исключения.
Вы всегда можете загрузить данные с помощью ридера, а затем перебрать его. Затем вы можете проверить, существует ли столбец. Это будет менее производительно, но вам не понадобятся блоки try / catch (так что, возможно, он более эффективен для ваших нужд).
Это должно помочь:
Public Shared Function ReaderContainsColumn(ByVal reader As IDataReader, ByVal name As String) As Boolean
For i As Integer = 0 To reader.FieldCount - 1
If reader.GetName(i).Equals(name, StringComparison.CurrentCultureIgnoreCase) Then Return True
Next
Return False
End Function
или (в C#)
public static bool ReaderContainsColumn(IDataReader reader, string name)
{
for (int i = 0; i < reader.FieldCount; i++) {
if (reader.GetName(i).Equals(name, StringComparison.CurrentCultureIgnoreCase)) return true;
}
return false;
}
: o)
Вы также можете использовать IDataReader.GetSchemaTable, чтобы получить список всех столбцов в считывателе.
http://support.microsoft.com/kb/310107
GetSchemaTable не перечисляет столбцы возвращаемой структуры данных.
Да, это так. Первый элемент в каждой строке из reader.GetSchemaTable (). Строки - это имена столбцов. т.е. reader.GetSchemaTable (). Rows [0] [0] дает мне имя первого столбца.
В прошлый раз, когда я пробовал это, он дал мне столбцы типа schema_info. Мне придется попробовать еще раз, так как это было давно.
Не уверен, почему люди голосуют против вас - это лучшее решение
Согласитесь с Джимом, легко лучшее решение
Не уверен, что это самый лучший вариант. Контрастность reader.GetSchemaTable().Rows.Cast<DataRow>().Select(x => (string)x["ColumnName"]).Contains(colName, StringComparer.OrdinalIgnoreCase)сEnumerable.Range(0, reader.FieldCount).Select(reader.GetName).Contains(colName, StringComparer.OrdinalIgnoreCase). Подход FieldCount не только чище, но и во много раз быстрее.
Лучшее решение, которое я использовал, - это сделать так:
DataTable dataTable = new DataTable();
dataTable.Load(reader);
foreach (var item in dataTable.Rows)
{
bool columnExists = item.Table.Columns.Contains("ColumnName");
}
Попытка получить к нему доступ через читатель ["ColumnName"] и проверка на null или DBNull вызовет исключение.
Enumerable.Range(0, reader.FieldCount).Any(i => reader.GetName(i) == "ColumnName")
Это будет плохо работать