DataReader - жесткие коды - PullRequest
15 голосов
/ 21 мая 2010

При возврате данных из DataReader я обычно использовал бы порядковый номер ссылки на DataReader, чтобы получить соответствующий столбец:

if (dr.HasRows)         
   Console.WriteLine(dr[0].ToString());

или

if (dr.HasRows)         
   Console.WriteLine(dr.GetString(0));

или

if (dr.HasRows)         
   Console.WriteLine((string)dr[0]);

Я всегда делал это, потому что на ранней стадии мне советовали, что использование dr["ColumnName"] или более элегантный способ индексации приводит к снижению производительности.

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

Какой самый надежный способ вернуть данные из DataReader?

Ответы [ 6 ]

25 голосов
/ 24 мая 2010

В этой ситуации можно поспорить обе стороны.Как уже указывалось другими, использование имени более читабельно и не сломается, если кто-то изменит порядок столбцов в базовой базе данных.Но можно также утверждать, что использование ординала имеет то преимущество, что он не ломается, если кто-то изменяет имя столбца в базовой базе данных.Однако я предпочитаю первый аргумент и думаю, что аргумент читабельности для имен столбцов превосходит второй аргумент в целом.И дополнительным аргументом для имен является то, что он может «самостоятельно обнаруживать» ошибки.Если кто-то действительно поменяет имя поля, то у кода больше шансов взлома, чем при обнаружении незаметной ошибки при работе, когда он читает неправильное поле.

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

SELECT name, address, phone from mytable

В этом случае его будет достаточно безопасно использоватьпорядковые номера для доступа к данным.Неважно, если кто-то перемещает поля в таблице.И если кто-то меняет имя, то оператор SQL выдает ошибку при запуске.

И последний момент.Я только что провёл тест на провайдере, которому помог написать.Тест прочитал миллион строк и получил доступ к полю «фамилия» в каждой записи (по сравнению со значением).Использование rdr[“lastname”] заняло 3301 миллисекунду для обработки, в то время как rdr.GetString(1) заняло 2640 миллисекунд (примерно 25% ускорение).В этом конкретном поставщике поиск имени использует отсортированный поиск для перевода имени в порядковый номер.

15 голосов
/ 22 мая 2010

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

var dr = command.ExecuteQuery();
if (dr.HasRows)
{
    //Get your ordinals here, before you run through the reader
    int ordinalColumn1 = dr.GetOrdinal("Column1");
    int ordinalColumn2 = dr.GetOrdinal("Column2");
    int ordinalColumn3 = dr.GetOrdinal("Column3");

    while(dr.Read())
    {
        // now access your columns by ordinal inside the Read loop. 
        //This is faster than doing a string column name lookup every time.
        Console.WriteLine("Column1 = " + dr.GetString(ordinalColumn1);
        Console.WriteLine("Column2 = " + dr.GetString(ordinalColumn2);
        Console.WriteLine("Column3 = " + dr.GetString(ordinalColumn3);
    }
}

Примечание: это действительно имеет смысл только для тех читателей, у которых вы ожидаете приличное количество строк. Вызовы GetOrdinal() являются дополнительными и оплачиваются только в том случае, если ваша совокупная экономия от вызова GetString(int ordinalNumber) в цикле больше чем стоимость звонка GetOrdinal.

Изменить: пропустил вторую часть этого вопроса. Что касается значений DBNull, я начал писать методы расширения, которые имеют дело с этой возможностью. пример: dr.GetDatetimeSafely() В этих методах расширения вы можете делать все, что вам нужно, чтобы быть уверенным, что вы получите ожидаемое значение.

4 голосов
/ 21 мая 2010

Я всегда использую подход с использованием строковых имен только потому, что чтение кода чище. Ментально разобрать индекс по имени столбца ужасно.

3 голосов
/ 21 мая 2010

Индексирование считывателя данных по имени немного дороже. Для этого есть две основные причины.

  • Типичные реализации хранят информацию о полях в структуре данных, которая использует числовую индексацию. Для преобразования имени в число необходимо выполнить операцию сопоставления.
  • В некоторых реализациях поиск по имени выполняется в два прохода. Первый проход пытается сопоставить имена полей с включенной чувствительностью к регистру. Если этот проход не удался, то второй проход начинается с выключенной чувствительности к регистру.

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

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

  • Код легче читать.
  • Код более терпим к изменениям в схеме набора результатов.

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

2 голосов
/ 21 мая 2010

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

Для каждого из полей вам нужно будет вручную проверять наличие нулей.

var dr = command.ExecuteQuery();

if (dr.HasRows) {
    var customer = new Customer();
    // I'm assuming that you know each field's position returned from your query.
    // Where comes the importance to write all of your selected fields and not just "*" or "ALL".
    customer.Id = dr[0] == null || dr[0] == DBNull.Value ? null : Convert.ToInt32(dr[0]);
    ...
}

В дополнение к этому, это позволит вам использовать отражение и сделать этот метод "GetData ()" более общим, предоставляя typeof (T) и получая правильный конструктор для правильного типа. Привязка к порядку каждого столбца - это единственное, чего некоторые хотят избежать, но в этом случае оно становится достойным.

0 голосов
/ 21 мая 2010

Проблема с порядковым номером заключается в том, что если порядок столбцов изменяется, и вы вынуждены изменить код потребителя для DataReader, в отличие от использования имен столбцов.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...