2010-03-10 15 views
8

El DataReader es más eficiente que una DataTable si solo necesita mostrar datos pero no manipularlos. Sin embargo, para obtener un DataReader de la capa de acceso a datos, ¿debo dejar abierto el objeto de conexión? Creo que este es también un problema de eficiencia muy grande. Entonces, ¿hay otro enfoque para esto con el fin de aprovechar al máximo el DataReader?Tabla de datos ADO.NET frente a lector de datos

Respuesta

10

Sí, el lector de datos es definitivamente el más eficiente, pero lo hace no ¡quiero mantener una conexión abierta durante un largo período de tiempo!

  • utilice DataReader para leer sus datos en un objeto entidad; abra la conexión, lea los datos, cierre la conexión
  • haga lo que tenga que hacer con su objeto comercial
  • almacene los cambios de nuevo, p. mediante el uso de una consulta SQL ad-hoc, un procedimiento almacenado, o cualquier otra cosa que desee; de nuevo: abra la conexión, vuelva a escribir los cambios, cierre la conexión

Esto es probablemente lo más eficiente que puede obtener, es un poco de trabajo, un código aburrido, y todo, pero es casi tan rápido como puede ser.

Si está más interesado en la productividad del desarrollador que en la velocidad bruta, ¿por qué no utilizar algún tipo de ORM para hacer todo este mapeo molesto y aburrido hacia adelante y hacia atrás? Guarda ¡mucha codificación y cosas desordenadas para mantener!

+0

Gracias por la respuesta. Normalmente uso ORM o creo mi capa de lógica de negocios personalizada con mis objetos de entidad. Sin embargo, para un proyecto pequeño intenté simplemente usar el lector de datos. ¿Hay algo que pueda hacer excepto objetos orm o de entidad? – erasmus

+2

@erasmus Si realmente es un proyecto pequeño, no se preocupe por la eficiencia. Solo usa lo que sea más fácil para ti. No existe una regla que requiera que cada aplicación funcione tan bien como sea posible. La única expectativa es que rinda tan bien como lo necesita para cumplir con las necesidades esperadas. –

+0

@erasmus: acuerde con Mike: cuanto más pequeño es el proyecto, menos crucial es el último rendimiento. Use Linq-to-SQL, NHibernate o algo así - es * mucho * más fácil hacer cosas que definitivamente es más importante en un pequeño proyecto –

0

Si desea abstraer completamente las conexiones y la ceremonia de ADO.NET, el DataReader es un pequeño desafío. Realmente no me gusta que mi herramienta de datos tenga una conexión abierta suelta, con la esperanza de que esté dispuesto el DataReader (suponiendo que utilizó la opción CommandBehavior.CloseConnection). Además, cuando se utilizan muchos DataReaders, es difícil agrupar conexiones, ya que no se puede hacer nada con la conexión hasta que se cierre el DataReader anterior. No se pueden pasar fácilmente. Su herramienta de datos no es una verdadera abstracción.

Las tablas de datos por otro lado son extremadamente flexibles y pueden dar como resultado un código muy eficiente y claro. Linq-To-DataTable es genial. Afortunadamente, la DataTable es actually pretty efficient. Para conjuntos de resultados no grandes, es casi tan rápido como el lector de datos. (Depende, por supuesto, de lo que está haciendo exactamente). Cada vez obtengo más DataTables de mi herramienta de datos en lugar de Readers. Realmente hace la vida simple. Puedo continuar usando la misma conexión abierta. No hay un "estado" en la herramienta de datos.

El código para obtener un DataReader es muy simple. Entonces, cuando realmente necesito un DataReader (no a menudo), simplemente dejo que mi DAL me proporcione mi conexión y obtengo mi DataReader.

+2

Las tablas de datos son una forma perezosa de devolver datos y conducir a un código que no se puede mantener. Deberías usar Readers y devolver objetos. –

+1

@ Keith Rousseau, parece que vamos a estar en desacuerdo. Para crear instancias de una o una pequeña cantidad de objetos, las tablas de datos proporcionan simplicidad y flexibilidad. Tal vez querrás tus datos como XML? De DataTable, no hay problema. La eficiencia que obtiene de DataReaders suele ser pequeña y no vale la pena. –

+0

Puedo encontrar muchos ejemplos. Con DataRows puede tener una fantástica flexibilidad en la construcción de sus objetos; puedes construir una tonelada de objetos desde una consulta db y una sola declaración LINQ. La eficiencia que obtiene de DataReaders suele ser pequeña y no vale la pena. Si por * lazy * te refieres a construir muchos objetos con una ** pequeña ** cantidad de código que es muy claro y sin posibilidad de que los programadores junior dejen las conexiones abiertas, entonces sí, soy flojo. Quiero que se haga rápido, claro y sin problemas. –

2

Deje que su capa de datos devuelva objetos, no datatables o lectores de datos. Sugeriría que llene sus objetos usando un lector de datos.

+0

¿Está bien que mi capa de vista conozca los objetos de mi dominio? – jim

+0

no hay ningún objeto de dominio aquí si se refiere a entidades. – erasmus

+2

@jim - esa es una pregunta completamente separada, pero la respuesta es típicamente no. –

1

Lo que suelo hacer es abrir el lector con CommandBehavior.CloseConnection. Luego corro por el lector y leo los datos en mi propio modelo o lista de objetos o lo que sea en la memoria con los datos y luego cierro el lector. Hace mucho de lo mismo que una tabla de datos, pero odio lidiar con estructuras de datos infladas y poco escritas.

0

Cuando investigué esto antes, creo que descubrí que la diferencia de rendimiento entre DataReader y DataTable era trivial, excepto quizás por grandes cantidades de datos.Desde entonces, normalmente he usado DataTable ya que tiene más funciones completas, se puede trabajar con desconectado, etc.

+1

¿Cuáles fueron los resultados? ¿puedes dar nos? – erasmus

3

Nunca dejo que un DataReader salga al aire libre (fuera del DAL). Solo es cuestión de tiempo antes de que deje conexiones abiertas en algún lugar. Además, casi nunca trato con tanta información en una sola llamada donde pasar alrededor de un DataTable o DataSet presenta un problema.

Estamos utilizando un lenguaje orientado a objetos y el DAL realmente puede aprovechar esto. Solo debe haber una línea de código en su proyecto que obtenga la cadena de conexión. Solo un objeto que realmente toca la base de datos (llamadas ExecuteNonQuery, DA.Fill(), etc.)

Esto también le permite involucrarse bastante con las excepciones de registro, etc. porque solo lo hace una vez. Así que en la clase base DAL que uso para todo mi objeto DAL en todo mi proyecto, tengo la lógica de que si el DAL arroja una excepción, entonces se registra en una tabla en mi base de datos. Este inicio de sesión falla en un archivo de texto si el registro de la base de datos falla.

So the code I see a lot looks like: 
- Start a try block 
- Make a SQLCommand 
- Get connection string. 
- Make Connection object 
- Open the connection 
- Get the data 
- Bind the data 
- Close the connection 
- Log error if exception 

Desde que encapsular todo esto, mi código para obtener los datos ahora se ve así:

GridView1.DataSource = cProgram.DB.getMyData(); 

(o más probablemente un objeto BAL se sitúa entre el 2). El DB es un objeto normal (no estático), pero solo instancia una vez por aplicación.

+0

¿Qué es un bal? – nawfal

+0

@nawfal Business Application Layer – JBrooks

+1

nunca escuché hablar de ese término. Se usa para "business layer", "domain", "model", etc. más. Espero que sean lo mismo. – nawfal

1

Tengamos un punto de referencia tonto para comprobar cuánto más rápido es el DataReader (.NET versión 4). Cogí un registro de la base de datos (SQL Server 2000) y leí todos sus campos. Repetí este proceso 1000 veces. DataReader tardó 17.3327585 segundos y DataTable tomó 18.37320156, por lo que DataReader es ~ 1.04 segundos más rápido que DataTable para 1000 lecturas.

Entonces, uno obtendría una ganancia de rendimiento de 0.00104 segundos si se prefiere DataReader sobre DataTable.

Mira Is DataSet slower than DataReader due to…? así

0

Straight from the documentation:

Cuando necesite recuperar varias filas de datos para que pueda pantalla o procesar los datos de alguna otra manera, usted tiene dos básica elecciones. Puede usar un objeto DataSet o un objeto DataReader.

El enfoque de DataReader es generalmente más rápido porque evita la sobrecarga que está asociada con la creación de un objeto DataSet. La sobrecarga asociada a un objeto DataSet incluye la creación de subobjetos DataSet tales como DataTables, DataRows y DataColumns. Sin embargo, el DataReader proporciona menos flexibilidad, y es menos adecuado a las situaciones donde tiene que almacenar en caché los datos y pasar los datos a los componentes en una aplicación que tiene varios niveles.

Nota: El DataAdapter utilizado para llenar el DataSet utiliza un DataReader internamente.

Utilizar un DataReader cuando se cumplen las siguientes condiciones:

• Es necesario sólo avance, acceso de sólo lectura a los datos (la manguera escenario de incendio), y desea tener acceso a los datos lo antes como sea posible, y no es necesario que lo guarde en caché.

• Tiene un contenedor de datos, como un componente de negocio que se puede poner los datos en

utilizar un conjunto de datos cuando se cumplen las siguientes condiciones:.

• Hay que almacenar en caché o pasar los datos entre las capas.

• Se requiere una vista relacional en memoria de los datos para XML o manipulación no XML.

• Desea actualizar algunas o todas las filas recuperadas, y desea utilizar las instalaciones de actualización de lotes de la clase SqlDataAdapter.

• Debe vincular los datos a un tipo de control que el DataReader no puede enlazar a . Muchos controles de Windows Forms capaces de enlace de datos requieren una fuente de datos que implemente la interfaz IList. El DataSet implementa IList, pero el DataReader implementa IEnumerable. IEnumerable admite el enlace de datos a la mayoría de los controles de Web Form pero no a ciertos controles de Windows Forms. Compruebe los requisitos de fuente de datos para el tipo de control particular que desea vincular.

• Tiene que acceder a varios conjuntos de datos al mismo tiempo, y usted no desea mantener abiertos los recursos del servidor.

Aunque hablando de DataSet, básicamente, la mayor parte se aplica a DataTable también. Desde el punto de vista de la eficiencia, here is rare benchmarking from msdn itself. El resultado final es DataReader es marginalmente más rápido, y si es importante ...

See this related question que sugiere algunos ORM interesantes y benchmarking.

0

Esto es lo mismo que publiqué here.

he hecho un poco a mí mismo con la evaluación comparativa de diversos enfoques:

public DataTable Read1(string query) 
{ 
    using (var cmd = conn.CreateCommand()) 
    { 
     cmd.CommandText = query; 
     cmd.Connection.Open(); 
     var table = new DataTable(); 
     using (var r = cmd.ExecuteReader()) 
      table.Load(r); 
     return table; 
    } 
} 

public DataTable Read2<S>(string query) where S : IDbDataAdapter, IDisposable, new() 
{ 
    using (var da = new S()) 
    { 
     using (da.SelectCommand = conn.CreateCommand()) 
     { 
      da.SelectCommand.CommandText = query; 
      da.SelectCommand.Connection.Open(); 
      DataSet ds = new DataSet(); 
      da.Fill(ds); 
      return ds.Tables[0]; 
     } 
    } 
} 

public IEnumerable<S> Read3<S>(string query, Func<IDataRecord, S> selector) 
{ 
    using (var cmd = conn.CreateCommand()) 
    { 
     cmd.CommandText = query; 
     cmd.Connection.Open(); 
     using (var r = cmd.ExecuteReader()) 
      while (r.Read()) 
       yield return selector(r); 
    } 
} 

public S[] Read4<S>(string query, Func<IDataRecord, S> selector) 
{ 
    using (var cmd = conn.CreateCommand()) 
    { 
     cmd.CommandText = query; 
     cmd.Connection.Open(); 
     using (var r = cmd.ExecuteReader()) 
      return ((DbDataReader)r).Cast<IDataRecord>().Select(selector).ToArray(); 
    } 
} 

public List<S> Read5<S>(string query, Func<IDataRecord, S> selector) 
{ 
    using (var cmd = conn.CreateCommand()) 
    { 
     cmd.CommandText = query; 
     cmd.Connection.Open(); 
     using (var r = cmd.ExecuteReader()) 
     { 
      var items = new List<S>(); 
      while (r.Read()) 
       items.Add(selector(r)); 
      return items; 
     } 
    } 
} 

1 y 2 vuelve DataTable mientras que el resto inflexible de tipos conjunto de resultados, por lo que su exactitud no manzanas con manzanas, pero mientras el tiempo en consecuencia.

sólo lo esencial:

Stopwatch sw = Stopwatch.StartNew(); 
for (int i = 0; i < 100; i++) 
{ 
    Read1(query); // ~8900 - 9200ms 

    Read1(query).Rows.Cast<DataRow>().Select(selector).ToArray(); // ~9000 - 9400ms 

    Read2<MySqlDataAdapter>(query); // ~1750 - 2000ms 

    Read2<MySqlDataAdapter>(query).Rows.Cast<DataRow>().Select(selector).ToArray(); // ~1850 - 2000ms 

    Read3(query, selector).ToArray(); // ~1550 - 1750ms 

    Read4(query, selector); // ~1550 - 1700ms 

    Read5(query, selector); // ~1550 - 1650ms 
} 

sw.Stop(); 
MessageBox.Show(sw.Elapsed.TotalMilliseconds.ToString()); 

La consulta regresaron alrededor de 1200 filas y 5 campos (duración de 100 veces). Además de Read1, todos funcionaron bien. De todos prefiero Read3 que devuelve datos de forma perezosa, como se enumera. Esto es ideal para la memoria si solo necesita enumerarla. Para tener una copia de la colección en la memoria, está mejor con Read4 o Read5 a su gusto.

Cuestiones relacionadas