Transacciones en .net

¿Cuáles son las mejores prácticas para realizar transacciones en C # .Net 2.0? ¿Cuáles son las clases que deberían usarse? ¿Cuáles son los peligros a tener en cuenta, etc. Todo lo que compromete y revierte cosas? Estoy empezando un proyecto en el que podría necesitar hacer algunas transacciones mientras inserto datos en el DB. Cualquier respuesta o enlace incluso para cosas básicas sobre transacciones son bienvenidas.

Hay 2 tipos principales de transacciones; transacciones de conexión y transacciones ambientales. Una transacción de conexión (como SqlTransaction) está vinculada directamente a la conexión db (como SqlConnection), lo que significa que debe seguir pasando la conexión, aceptar en algunos casos, pero no permite “crear / usar / liberar” uso, y no permite el trabajo cross-db. Un ejemplo (formateado por espacio):

using (IDbTransaction tran = conn.BeginTransaction()) { try { // your code tran.Commit(); } catch { tran.Rollback(); throw; } } 

No demasiado desordenado, pero limitado a nuestra conexión “conn”. Si queremos llamar a diferentes métodos, ahora necesitamos pasar “conn”.

La alternativa es una transacción ambiental; nuevo en .NET 2.0, el objeto TransactionScope (System.Transactions.dll) permite el uso en un rango de operaciones (los proveedores adecuados se alistarán automáticamente en la transacción ambiental). Esto hace que sea fácil adaptarse al código existente (no transaccional) y hablar con múltiples proveedores (aunque DTC se involucrará si usted habla con más de uno).

Por ejemplo:

 using(TransactionScope tran = new TransactionScope()) { CallAMethodThatDoesSomeWork(); CallAMethodThatDoesSomeMoreWork(); tran.Complete(); } 

Tenga en cuenta que los dos métodos pueden manejar sus propias conexiones (abrir / usar / cerrar / disponer), sin embargo, se convertirán silenciosamente en parte de la transacción ambiental sin que tengamos que pasar nada.

Si los errores de su código, se llamará a Dispose () sin Complete (), se revertirá. El anidamiento esperado, etc., es compatible, aunque no puede deshacer una transacción interna y completar la transacción externa: si alguien no está contento, la transacción se cancela.

La otra ventaja de TransactionScope es que no está atada solo a las bases de datos; cualquier proveedor de transacciones puede usarlo. WCF, por ejemplo. O incluso hay algunos modelos de objetos compatibles con TransactionScope (es decir, clases .NET con capacidad de reversión, quizás más fácil que un recuerdo, aunque nunca he usado este enfoque).

En general, un objeto muy, muy útil.

Algunas advertencias:

  • En SQL Server 2000, un TransactionScope irá a DTC inmediatamente; esto se soluciona en SQL Server 2005 y posteriores, puede usar el LTM (mucho menos gastos generales) hasta que hable con 2 fonts, etc., cuando se eleva a DTC.
  • Hay un error que significa que es posible que deba modificar su cadena de conexión
 protected void Button1_Click(object sender, EventArgs e) { using (SqlConnection connection1 = new SqlConnection("Data Source=.\\SQLEXPRESS;AttachDbFilename=|DataDirectory|\\Database.mdf;Integrated Security=True;User Instance=True")) { connection1.Open(); // Start a local transaction. SqlTransaction sqlTran = connection1.BeginTransaction(); // Enlist a command in the current transaction. SqlCommand command = connection1.CreateCommand(); command.Transaction = sqlTran; try { // Execute two separate commands. command.CommandText = "insert into [doctor](drname,drspecialization,drday) values ('a','b','c')"; command.ExecuteNonQuery(); command.CommandText = "insert into [doctor](drname,drspecialization,drday) values ('x','y','z')"; command.ExecuteNonQuery(); // Commit the transaction. sqlTran.Commit(); Label3.Text = "Both records were written to database."; } catch (Exception ex) { // Handle the exception if the transaction fails to commit. Label4.Text = ex.Message; try { // Attempt to roll back the transaction. sqlTran.Rollback(); } catch (Exception exRollback) { // Throws an InvalidOperationException if the connection // is closed or the transaction has already been rolled // back on the server. Label5.Text = exRollback.Message; } } } } 

También podría envolver la transacción en su propio procedimiento almacenado y manejarlo de esa manera en lugar de realizar transacciones en C #.

si solo lo necesita para cosas relacionadas con DB, algunos O Mappers (por ej., NHibernate) admiten Transactinos de fábrica por defecto.

También depende de lo que necesites. Para las transacciones SQL básicas, puede intentar realizar transacciones TSQL utilizando BEGIN TRANS y COMMIT TRANS en su código. Esa es la manera más fácil, pero tiene complejidad y debe tener cuidado de comprometerse correctamente (y deshacer).

Yo usaría algo como

 SQLTransaction trans = null; using(trans = new SqlTransaction) { ... Do SQL stuff here passing my trans into my various SQL executers ... trans.Commit // May not be quite right } 

Cualquier falla lo sacará del using y la transacción siempre se confirmará o revertirá (dependiendo de lo que le diga que haga). El mayor problema al que nos enfrentamos fue asegurarnos de que siempre se haya comprometido. El uso asegura que el scope de la transacción es limitado.