2010-05-21 13 views
15

Al devolver datos de un DataReader yo usaría normalmente la referencia ordinal en el DataReader para agarrar la columna correspondiente:DataReader - ordinales de código duro?

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

o

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

o

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

que tienen siempre hecho esto porque me avisaron en una etapa temprana que usando dr["ColumnName"] o más el Una forma elegante de indexar causa un golpe de rendimiento.

Sin embargo, aunque todas las referencias a las entidades de datos son cada vez más fuertemente tipadas, me siento más incómodo con esto. También sé que lo anterior no marca el DBNull.

¿Cuál es la forma más robusta de devolver datos desde un DataReader?

+0

pregunta relacionada con el rendimiento: [sqldatareader-are-these-two-the-same-which-one-is-faster] (http: // stackoverflow.com/questions/7831574/sqldatareader-are-these-two-the-same-which-one-is-faster) – nawfal

Respuesta

25

Es posible discutir ambas partes en esta situación. Como ya lo señalaron otros, usar el nombre es más legible y no se romperá si alguien cambia el orden de las columnas en la base de datos subyacente. Pero también se podría argumentar que el uso de un ordinal tiene la ventaja de no romperse si alguien cambia el nombre de la columna en la base de datos subyacente. Sin embargo, prefiero el argumento anterior y creo que el argumento de legibilidad para los nombres de columna supera al segundo argumento en general. Y un argumento adicional para los nombres es que puede "autodetectar" errores. Si alguien cambia el nombre de un campo, entonces el código tiene una mayor probabilidad de romperse en lugar de tener la sutil falla de parecer que funciona mientras lee el campo equivocado.

Parece obvio, pero tal vez valga la pena mencionar un caso de uso que tiene el error de autodetección y el rendimiento de los ordinales. Si se especifica la lista SELECT de manera explícita en el SQL, a continuación, utilizando los ordinales no será un problema, ya que la declaración contenida en el código garantiza el orden:

SELECT name, address, phone from mytable 

En este caso, sería bastante seguro de usar a los ordinales acceder a los datos. No importa si alguien mueve los campos alrededor de la mesa. Y si alguien cambia un nombre, la sentencia SQL produce un error cuando se ejecuta.

Y un punto final. Acabo de realizar una prueba con un proveedor que ayudé a escribir. La prueba leyó 1 millón de filas y accedió al campo "apellido" en cada registro (comparado con un valor). El uso de rdr[“lastname”] tomó 3301 milisegundos para procesar mientras que rdr.GetString(1) tomó 2640 milisegundos (aproximadamente un 25% de aceleración). En este proveedor en particular, la búsqueda del nombre utiliza una búsqueda ordenada para traducir el nombre a ordinal.

+0

Aquí tiene algunos puntos buenos. –

+4

+1 para dar métricas. –

+0

Personalmente, creo que es más común reorganizar las columnas (leer, insertar nuevos campos y reorganizar las posiciones de las columnas existentes) que cambiar el nombre de los nombres de las columnas. Entonces +1 por su sugerencia. – nawfal

0

El problema con el ordinal es si el orden de las columnas cambia y se ve obligado a modificar el código de consumidor para el DataReader, a diferencia del uso de nombres de columna.

Yo no creo que haya una ganancia de rendimiento cuando se utilizan nombres ordinales o columna, es más sobre las mejores prácticas y estándares de codificación y capacidad de mantenimiento de código realmente

4

Siempre voy con el enfoque de cadena nombre sólo porque la lectura del código es limpiador. Tener que analizar mentalmente el índice del nombre de la columna es horrible.

+4

El uso del nombre de columna también elimina los "números mágicos" y facilita el trabajo de los programadores. Usar el ordinal es una micro-optimización prematura. – Malfist

+1

+1. Los nombres de columna cambian más raramente que su orden – abatishchev

+0

Este aspecto tiene dos partes, el aspecto de rendimiento y la facilidad de mantenimiento/legibilidad. Los nombres de las cadenas no se escalan muy bien. @Malfist etiquetando algo como una "micro-optimización prematura" es relativa a su código. Si su código es de alto rendimiento, este podría ser el cuello de botella. – Josh

2

Creo que los campos indexados es el mejor enfoque, si solo fuera para evitar cambios en los nombres de campo de la base de datos subyacente, lo que requeriría una recompilación de su aplicación porque codificó el nombre del campo.

Para cada uno de los campos, deberá verificar manualmente los nulos.

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]); 
    ... 
} 

Además de ello, se le permitiría utilizar la reflexión y hacer que proporciona este método "GetData()" más genérico de un typeof (T) y conseguir el constructor adecuado para el tipo adecuado. El enlace al orden de cada columna es la única cosa que algunos desean evitar, pero en este caso, se vuelve valioso.

4

Indexar el lector de datos por nombre es un poco más caro. Existen dos motivos principales para esto.

  • Las implementaciones típicas almacenan información de campo en una estructura de datos que usa indexación numérica. Debe llevarse a cabo una operación de mapeo para transponer un nombre a un número.
  • Algunas implementaciones harán una búsqueda de dos pasos en el nombre. La primera pasada intenta hacer coincidir los nombres de campo con la distinción entre mayúsculas y minúsculas activada. Si ese pase falla, un segundo pase comienza con la desactivación de la mayúscula y la minúscula.

Sin embargo, en la mayoría de los casos, la penalización de rendimiento incurrida al buscar un campo por nombre queda eclipsada por la cantidad de tiempo que le lleva a la base de datos ejecutar el comando. No permita que la penalización de rendimiento dicte su elección entre el nombre y la indexación numérica.

A pesar de la leve penalidad de rendimiento, normalmente elijo la indexación de nombres por dos razones.

  • El código es más fácil de leer.
  • El código es más tolerante a los cambios en el esquema del conjunto de resultados.

Si usted se siente como la reducción del rendimiento del nombre de indexación está convirtiendo en un problema (tal vez el comando se ejecuta rápidamente, pero devuelve un montón de registros) a continuación, buscar el índice numérico vez por su nombre, salvar a la basura, y utilizarla para las filas restantes.

13

La búsqueda del nombre de cadena es mucho más costosa que la llamada ordinal, pero más fácil de mantener y menos "frágil" que la codificación difícil de los ordinales. Así que aquí es cómo lo he estado haciendo. Es lo mejor de ambos mundos. No tengo que recordar los valores ordinales ni me importa si el orden de las columnas cambia, pero obtengo los beneficios de rendimiento de usar ordinales.

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); 
    } 
} 

Nota: esto sólo tiene un sentido práctico para los lectores que espera tener un buen número de filas de la GetOrdinal() llamadas son de pago, y sólo pagan por sí mismos si sus ahorros combinados de llamar GetString(int ordinalNumber) en el bucle son mayores. que el costo de llamar al GetOrdinal.

Editar: se perdió la segunda parte de esta pregunta. En cuanto a los valores DBNull, comencé a escribir métodos de extensión que tratan con esa posibilidad. ejemplo: dr.GetDatetimeSafely() Dentro de esos métodos de extensión, puede hacer lo que necesite para sentirse seguro de recuperar el valor esperado.

+0

Upvoted para lo mejor de ambos mundos. – nawfal

+1

Me gusta este enfoque cuando tengo que sumergirme en ADO.NET. Creo un objeto en línea y mapeo todos los ordinales a nombres amigables para poder hacer algo como 'reader.GetString (ordinals.CustomerName)' –

Cuestiones relacionadas