Ya casi está allí. Envuelve el código que envió en una función con esta firma:
IEnumerable<IDataRecord> MyQuery()
y luego vuelva a colocar su código // Do something with Reader
con esto:
yield return reader;
Ahora usted tiene 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 se 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 sus métodos de fábrica crean una copia como era de esperar, este código debe ser seguro para su uso en un bucle Parallel.ForEach. Al llamar al método sería algo como esto (suponiendo una clase a un empleado con un método de fábrica estática denominada "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 en este código como yo una vez fueUn 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.
Estoy contigo para la mayoría de lo que dijiste, me perdiste un poco en la fábrica. Func factory no coincide con la llamada cuando se usa con el factor de retorno yeild (rdr). Creo que se refería a Func . Así que no estoy seguro de lo que quiere decir con copiar como se esperaba. ¿Quiere decir básicamente leen del lector y devuelven una MyDataClass similar a lo que Reed estaba diciendo en su respuesta? –
También parece que su llamada a GetData es nuestra orden de tener la función de fábrica antes de la cadena sql. A pesar de que creo que lo entiendo, su empleado. Crear es su fábrica que hace el trabajo necesario con el lector. Voy a jugar con esto por un tiempo y ver cómo va. –
Sí, quise decir Func. Arreglará eso y el parámetro desajuste. –