.NET SqlDataReader Item [] против GetString (GetOrdinal())?

Используя класс SqlDataReader, что, если таковые имеются, являются функциональными различиями между:

(string) dataReader["MyFieldName"];

и

dataReader.GetString(dataReader.GetOrdinal("MyFieldName"));

Ответы

Ответ 1

Отбрасывая проблемы, для сингулярного вызова их нет. Индексатор отправит вызов DbDataReader.GetOrdinal, а затем вызовет соответствующий метод Get, чтобы получить значение (обратите внимание, что быстрее вызвать методы Get, используя ординал, чем использование индексатора с именем поля).

Однако это будет каждый раз проверять порядковый номер. Если вы повторяете несколько записей в режиме прямого доступа, только для чтения (это именно то, что предназначены DbDataReader), тогда вы может уменьшить накладные расходы этого поиска, сделав это только один раз.

Вы можете сделать это следующим образом:

// Move to the first record.  If no records, get out.
if (!dataReader.Read()) return;

// Before the loop.  Can do this for any other fields being
// accessed in the loop as well.
int myFieldNameOrdinal = dataReader.GetOrdinal("MyFieldName");

// Process the records.  Remember, already on the first record, so
// use do/while here.
do
{
    // Do something with your field.
    Console.WriteLine(dataReader.GetString(myFieldNameOrdinal));
} while (dataReader.Read());

Ответ 2

При работе с нулевым значением:

// Will throw an InvalidCastException 
// Exception Message will be "Unable to cast object of type System.DBNull
// to System.String 
(string) dataReader["MyFieldName"]; 

// Will throw a SqlNullValueException
// Exception Message will be "Data is Null. This method or property
// cannot be called on Null values."
dataReader.GetString(dataReader.GetOrdinal("MyFieldName"));

Ответ 3

В первом случае вы выполняете кастинг, который является особенно уродливым для типов значений (задействованный unboxing). Лично я всегда использую второе, и я бы рекомендовал вам.

Ответ 4

//Well, we want to avoid the null exception issue entirely.
//Let check for null first, before we try to use the value.

if( !dataReader.IsDBNull(dataReader.GetOrdinal("MyFieldName")))
{
//Store my data or use the value
string mystring=dataReader.GetString(dataReader.GetOrdinal("MyFieldName"));
}

Serendipity - прекрасный метод обнаружения.

Ответ 5

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

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

Начните с поля:

static readonly ConcurrentDictionary<string, int> OrdinalMap = 
            new ConcurrentDictionary<string, int>();

Затем обновите свой код доступа, похожий на:

reader.GetString(OrdinalMap.GetOrAdd("MyFieldName", reader.GetOrdinal))

Теперь у вас есть поиск в потоковом O (1) для ординалов без необходимости поддерживать какие-либо ручные карты переменных или константы, которые, если вы измените запрос, нарушаете мир. Не то чтобы это было важно, а просто для ясности из-за поведения GetOrAdd, если вы выполняете много запросов одновременно, возможно, что reader.GetOrdinal("MyFieldName") может выполняться несколько раз, а не точно один раз, но для всех целей и задач может рассматриваться как один раз.