2012-04-19 18 views
10

Estoy codificando para leer archivos xml para actualizar la base de datos. Obtengo unos 500 archivos xml y quiero procesarlos lo más rápido que pueda.¿Es posible ejecutar muchos procedimientos almacenados en una sola operación?

Todas las operaciones de la base de datos se realizan mediante procedimientos almacenados.

Hay aproximadamente 35 procedimientos almacenados diferentes llamados para cada archivo xml.

principio yo había escrito el código como este

var cmd = new SqlCommand("EXEC UpdateTeamStats("+teamId+","+points+")"); 
cmd.CommandType = CommandType.Text; 

pero después de pasar por algunas de las mejores prácticas que lo cambié a

var cmd = new SqlCommand("UpdateTeamStats"); 
cmd.CommandType = CommandType.StoredProcedure; 
cmd.Parameters.Add("teamId", 21); 
cmd.Parameters.Add("points", 2); 

debido al alto número de procedimientos almacenados que se llama desde el programa Me di cuenta de que tenía que hacer menos llamadas para optimizar.

Así que quiero recopilar todos los 35 procedimientos almacenados juntos y ejecutarlos de una vez.

Los procedimientos almacenados son diferentes con diferentes parámetros y no conozco una manera de recopilarlos y ejecutarlos juntos después de los cambios de parámetros que hice anteriormente.

Estaba pensando en llamar a un procedimiento almacenado gigante y dentro de ese procedimiento almacenado llamar a los otros 35, pero no soy muy bueno en SQL y me llevará a una complejidad innecesaria.

¿Es posible hacer esto completamente en C#?

O hay algún otro método mejor que hacer cola los StoredProcedures y ejecutarlas rápidamente

+0

Lo que hacer ¿Espera que ocurra cuando en medio de su ejecución, ha ocurrido un error? ¿Se revertirán todos los procedimientos ejecutados? ¿O simplemente continúa procesando los procedimientos restantes? –

+0

Tenga en cuenta que su título dice "en paralelo", pero al leer su pregunta, parece que solo quiere decir "como un comando de base de datos" –

+0

@GeorgeDuckett: esa fue mi edición. El original fue "de una vez". – Oded

Respuesta

0

Se puede crear un CommandQueue, a través de la Command pattern y crear un delegado en el comando que se asigna a lo que sus requisitos son para la llamada al procedimiento almacenado; por lo visto, algo así como:

public class CommandQueue 
{ 
    private Connection _connexion = new Connection(); // Set this up somehow. 

    // Other methods to handle the concurrency/ calling/ transaction etc. 

    public Func<string, Dictionary<string, int>, bool> CallStoredProcedure = (procedureName, parameterValueMap) => 
    { 
     cmd.Connection = GetConnexion(); 
     var cmd = new SqlCommand(procedureName); 
     cmd.CommandType = CommandType.StoredProcedure; 

     foreach (var parameterValueMapping in parameterValueMap) 
     { 
     cmd.Parameters.Add(parameterValueMapping.Key, parameterValueMapping.Value); 
     } 

     var success = cmd.ExecuteNonQuery(); 

     return success; 
    } 

    private Connection GetConnexion() 
    { 
     return _connexion; 
    } 
} 

Y entonces, la configuración CommandQueue, para que tenga un grupo de subprocesos, desde el que se puede llamar el delegado en un nuevo hilo, para que se ejecuten en paralelo.

En realidad, al mirar la clase SQLCommand, puede realizar llamadas asincrónicas. Por lo tanto, debe poder llamar a cada uno de sus procedimientos almacenados de forma asincrónica, configurar un delegado para cuando cada uno complete y envolverlo todo en una transacción, para que pueda deshacerlos cuando lo necesite llamando al Cancel() en cada comando. ¡Probablemente aún use un CommandQueue para resumir esto, ya que sugeriría que probablemente lo cambiarías más tarde!

Creo que todavía encapsularía las llamadas a procedimientos almacenados con un delegado en un CommandQueue, de modo que esto abstraiga los detalles del proceso almacenado y haga que el proceso sea más fácil de entender para cualquier otra persona y también más fácil de mantener. Será mucho más fácil si agrega nuevos procs almacenados, o cambia el nombre, o algo. Puede configurar una lista estática puede contener todos los delegados, o una lista estática con los detalles del procedimiento almacenado necesarios y utilizar el delegado para transferir solo los parámetros.

1

favor Descarga del bloque de aplicaciones de datos de Microsoft desde

http://www.microsoft.com/download/en/details.aspx?id=435

bien, pero ¿cómo lo uso?

El uso de esta clase de contenedor es bastante simple.

DAC DC = new DAC(); 
DC.StoredProcedure = "nProc_InsertOrder"; 
DC.Params.Add("@OrderId", SqlDbType.VarChar, "Order1"); 
DC.Params.Add("@CustomerName", SqlDbType.VarChar, "test"); 
DAC.Commands.Add(DC); 

DC = new DAC(); 
DC.StoredProcedure = "nProc_InsertOrderLineItems"; 
DC.Params.Add("@OrderId", SqlDbType.VarChar, "Order1"); 
DC.Params.Add("@OrderLineId", SqlDbType.VarChar, "A1"); 
DAC.Commands.Add(DC); 

DC = new DAC(); 
DC.StoredProcedure = "nProc_InsertOrderLineItems"; 
DC.Params.Add("@OrderId", SqlDbType.VarChar, "Order1"); 
DC.Params.Add("@OrderLineId", SqlDbType.VarChar, "A2"); 
DAC.Commands.Add(DC); 

DC = new DAC(); 
DC.StoredProcedure = "nProc_CreateBill"; 
DC.Params.Add("@BillDate", SqlDbType.DateTime, DateTime.Now); 
DC.Params.Add("@BillId", SqlDbType.VarChar, "Bill1"); 
DAC.Commands.Add(DC); 
DAC.ExecuteBatch(); 

Si la inserción del pedido falla, la factura no debe crearse. Del mismo modo, si las líneas de pedido son erróneas, no se debe crear la orden. Estamos logrando esto en solo unas pocas líneas de código a través de ADO.Net.

En este ejemplo, hasta que llamemos a ExecuteBatch, en realidad no estamos insertando los registros, sino preparando el objeto para realizar las actualizaciones por lotes.

+0

¿De dónde viene el objeto DAC principal, el que estamos usando para 'DAC.Commands.Add'? ¡No puede ser estático, esto no sería seguro para subprocesos! ¿Y esto realmente hace lo que queremos aquí? Sin el apoyo explícito de las clases de cliente de SQL Server, ¿esto realmente solo hace un ida y vuelta DB simple o en su lugar, envuelve todos los comandos en una transacción? – Rup

+0

Estamos ajustando todos los comandos en un solo objeto y ejecutándolos en una única conexión abierta con la transacción. –

1

La mejor solución sería escribir un único procedimiento almacenado con un parámetro de tabla aprobado que contiene una lista de todos los parámetros por archivo xml. Luego, en este procedimiento almacenado, llame a todos los demás procesos almacenados para cada registro en el parámetro con valores de tabla.

Si esto no está bien, entonces podría usar un comando Sql de tipo texto en lugar del procedimiento almacenado y simplemente compilar el comando sobre la marcha y ejecutarlo. Puede usar parámetros como lo hace ahora o simplemente puede escribir sql dinámico.

0

En lo personal, y en mi experiencia con ADO.NET, creo que no tendrá ningún problema en absoluto la ejecución de estos como declaraciones separadas utilizando una sola SqlConnection.

Esto tiene la ventaja de aprovechar .NET increíble connection pooling, le permite trabajar/personalizar/interactuar con cada comando individualmente y utiliza una conexión compartida (reduciendo la cantidad de corretaje de conexión a una cantidad trivial).

También me gustaría destacar la importancia de las cláusulas using aquí que facilitan la eliminación adecuada de los diversos recursos.

Por ejemplo:

using (var conn = new SqlConnection("connection string")) 
{ 
    using (var cmd = new SqlCommand()) 
    { 
     cmd.Connection = conn; 
     cmd.CommandType = CommandType.StoredProcedure; 

     //ready to query 
     conn.Open(); 

     cmd.CommandText = "UpdateTeamStats"; 
     var teamIdParam = new SqlParameter("teamId", 21); 
     var pointsParam = new SqlParameter("points", 2); 

     cmd.Parameters.Add(teamIdParam); 
     cmd.Parameters.Add(pointsParam); 

     cmd.ExecuteNonQuery(); //OR if you're async cmd.ExecuteNonQueryAsync(); 

     //the rest of your executions 

     conn.Close(); 
    } 
} 

Y si estoy siendo solícita por un momento, se puede utilizar una biblioteca, como la mía DbConnect, lo que reduce la anterior a:

using (var db = new DbConnect("connection string")) 
{ 
    db.SetSqlCommand("UpdateTeamStats"); 
    db.AddParameter("teamId", 21); 
    db.AddParameter("points", 2); 

    db.ExecuteNonQuery().Wait(); //OR if youre async await db.ExecuteNonQuery(); 

    db.ClearParameters(); 
    db.SetSqlCommand("some other proc"); 

    //rest of executions 
} 
Cuestiones relacionadas