DataReader - ординалы hardcode?

При возврате данных из a 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.

Что самый устойчивый путь состоит в том, чтобы возвратить данные из a DataReader?

15
задан BIBD 9 February 2017 в 19:00
поделиться

6 ответов

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

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

SELECT name, address, phone from mytable

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

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

25
ответ дан 1 December 2019 в 00:23
поделиться

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

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

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

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

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

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

3
ответ дан 1 December 2019 в 00:23
поделиться

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

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

0
ответ дан 1 December 2019 в 00:23
поделиться

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

4
ответ дан 1 December 2019 в 00:23
поделиться

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

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

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) и получив соответствующий конструктор для правильного типа. Привязка к порядку каждого столбца - единственное, чего некоторые хотят избежать, но в этом случае это становится достойным.

2
ответ дан 1 December 2019 в 00:23
поделиться

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

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.

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

15
ответ дан 1 December 2019 в 00:23
поделиться
Другие вопросы по тегам:

Похожие вопросы: