while usar recuperar mediante executereader datos data como c# ado.net .net-4.0 task-parallel-library

c# - usar - ¿Hay alguna forma de utilizar la Biblioteca de tareas paralelas(TPL) con SQLDataReader?



sql data reader c# (2)

Tendrás dificultades para reemplazar ese ciclo while directamente. SqlDataReader no es una clase segura para subprocesos, por lo que no puede usarla directamente desde varios subprocesos.

Dicho esto, podría procesar los datos que lee utilizando el TPL. Hay algunas opciones, aquí. Lo más fácil podría ser hacer su propia IEnumerable<T> que funcione en el lector, y devuelva una clase o estructura que contenga sus datos. Luego puede usar PLINQ o una declaración Parallel.ForEach para procesar sus datos en paralelo:

public IEnumerable<MyDataClass> ReadData() { using (SqlConnection conn = new SqlConnection("myConnString")) using (SqlCommand comm = new SqlCommand("myQuery", conn)) { conn.Open(); SqlDataReader reader = comm.ExecuteReader(); if (reader.HasRows) { while (reader.Read()) { yield return new MyDataClass(... data from reader ...); } } } }

Una vez que tenga ese método, puede procesarlo directamente, a través de PLINQ o TPL:

Parallel.ForEach(this.ReadData(), data => { // Use the data here... });

O:

this.ReadData().AsParallel().ForAll(data => { // Use the data here... });

Me gusta la simplicidad de los métodos de extensión Parallel.For y Parallel.ForEach en TPL. Me preguntaba si había una forma de aprovechar algo similar o incluso con Tareas ligeramente más avanzadas.

A continuación se muestra un uso típico de SqlDataReader, y me preguntaba si era posible y, en caso afirmativo, cómo reemplazar el ciclo while a continuación con algo en el TPL. Debido a que el lector no puede proporcionar un número fijo de iteraciones, el método de extensión For no es posible, lo que deja el tratamiento de las tareas que reuniría. Esperaba que alguien ya haya abordado esto y haya averiguado algunas cosas que hacer y no con ADO.net.

using (SqlConnection conn = new SqlConnection("myConnString")) using (SqlCommand comm = new SqlCommand("myQuery", conn)) { conn.Open(); SqlDataReader reader = comm.ExecuteReader(); if (reader.HasRows) { while (reader.Read()) { // Do something with Reader } } }


Ya casi estás ahí. Envuelva el código que publicó en una función con esta firma:

IEnumerable<IDataRecord> MyQuery()

y luego reemplace su // Do something with Reader código de // Do something with Reader con esto:

yield return reader;

Ahora tienes algo que funciona en un solo hilo. Desafortunadamente, a medida que lee los resultados de la consulta, devuelve una referencia al mismo objeto cada vez, y el objeto simplemente muta para cada iteración. Esto significa que si intenta ejecutarlo en paralelo, obtendrá resultados realmente extraños, ya que las lecturas paralelas modifican el objeto utilizado en diferentes subprocesos. Necesita código para tomar una copia del registro para enviar a su bucle paralelo.

En este punto, sin embargo, lo que me gusta hacer es omitir la copia extra del registro e ir directamente a una clase fuertemente tipada. Más que eso, me gusta usar un método genérico para hacerlo:

IEnumerable<T> GetData<T>(Func<IDataRecord, T> factory, string sql, Action<SqlParameterCollection> addParameters) { using (var cn = new SqlConnection("My connection string")) using (var cmd = new SqlCommand(sql, cn)) { addParameters(cmd.Parameters); cn.Open(); using (var rdr = cmd.ExecuteReader()) { while (rdr.Read()) { yield return factory(rdr); } } } }

Suponiendo que sus métodos de fábrica crean una copia como se esperaba, este código debe ser seguro de usar en un bucle Parallel.ForEach. Llamar al método sería algo como esto (suponiendo que una clase de empleado con un método de fábrica estático llamado "Crear"):

var UnderPaid = GetData<Employee>(Employee.Create, "SELECT * FROM Employee WHERE AnnualSalary <= @MinSalary", p => { p.Add("@MinSalary", SqlDbType.Int).Value = 50000; }); Parallel.ForEach(UnderPaid, e => e.GiveRaise());

Actualización importante:
No estoy tan seguro de este código como lo fui antes. Un hilo separado podría mutar el lector mientras otro hilo está en el proceso de hacer su copia. Podría ponerle un candado, pero también me preocupa que otro hilo pueda llamar al lector una vez que el original haya llamado a Read () pero antes de que empiece a hacer la copia. Por lo tanto, la sección crítica aquí consiste en todo el ciclo while ... y en este punto, vuelves a un único subproceso nuevamente. Espero que haya una manera de modificar este código para que funcione como se espera para los escenarios de subprocesos múltiples, pero se necesitará más estudio.