c# - framework - transactionscope vb
TransactionScope completado prematuramente (7)
Tengo un bloque de código que se ejecuta dentro de un TransactionScope y dentro de este bloque de código hago varias llamadas al DB. Selecciona, actualiza, crea y elimina toda la gama. Cuando ejecuto mi eliminación, la ejecuto usando un método de extensión del SqlCommand que reenviará automáticamente la consulta si se bloquea, ya que esta consulta podría potencialmente llegar a un punto muerto.
Creo que el problema ocurre cuando se golpea un punto muerto y la función intenta volver a enviar la consulta. Este es el error que recibo:
La transacción asociada con la conexión actual se ha completado pero no se ha eliminado. La transacción debe eliminarse antes de que la conexión se pueda usar para ejecutar sentencias de SQL.
Este es el código simple que ejecuta la consulta (todo el código siguiente se ejecuta dentro del uso de TransactionScope):
using (sqlCommand.Connection = new SqlConnection(ConnectionStrings.App))
{
sqlCommand.Connection.Open();
sqlCommand.ExecuteNonQueryWithDeadlockHandling();
}
Aquí está el método de extensión que vuelve a enviar la consulta interbloqueada:
public static class SqlCommandExtender
{
private const int DEADLOCK_ERROR = 1205;
private const int MAXIMUM_DEADLOCK_RETRIES = 5;
private const int SLEEP_INCREMENT = 100;
public static void ExecuteNonQueryWithDeadlockHandling(this SqlCommand sqlCommand)
{
int count = 0;
SqlException deadlockException = null;
do
{
if (count > 0) Thread.Sleep(count * SLEEP_INCREMENT);
deadlockException = ExecuteNonQuery(sqlCommand);
count++;
}
while (deadlockException != null && count < MAXIMUM_DEADLOCK_RETRIES);
if (deadlockException != null) throw deadlockException;
}
private static SqlException ExecuteNonQuery(SqlCommand sqlCommand)
{
try
{
sqlCommand.ExecuteNonQuery();
}
catch (SqlException exception)
{
if (exception.Number == DEADLOCK_ERROR) return exception;
throw;
}
return null;
}
}
El error ocurre en la línea:
sqlCommand.ExecuteNonQuery();
Confirmado que este error también puede ser causado por un tiempo de espera de transacción. Solo para agregar a lo que Marcus + Rolf ha declarado, si no ha establecido explícitamente un tiempo de espera en el TransactionScope
, el tiempo de espera TimeSpan asumirá un valor predeterminado. Este valor predeterminado es el menor de:
Si ha anulado la
app.config
local deapp.config
/web.config
, por ej.<system.transactions> <defaultSettings timeout="00:05:00" /> </system.transactions>
Pero esto es ''limitado'' en la configuración
<machineSettings maxTimeout="00:10:00" />
Descubrí que este mensaje puede ocurrir cuando una transacción se ejecuta por un período más largo que el maxTimeout
para System.Transactions
. No importa que TransactionOptions.Timeout
aumente, no puede exceder maxTimeout
.
El valor predeterminado de maxTimeout
se establece en 10 minutos y su valor solo se puede modificar en machine.config
Agregue lo siguiente (en el nivel de configuración) a machine.config
para modificar el tiempo de espera:
<configuration>
<system.transactions>
<machineSettings maxTimeout="00:30:00" />
</system.transactions>
</configuration>
Machine.config se puede encontrar en: %windir%/Microsoft.NET/Framework/[version]/config/machine.config
Puede leer más sobre esto en esta publicación de blog: http://thecodesaysitall.blogspot.se/2012/04/long-running-systemtransactions.html
Esta excepción también puede ser causada por deshabilitar el Microsoft Distributed Transaction Coordinator
.
Si queremos habilitarlo, ejecutamos " dcomcnfg " y seleccionamos "Component Services" -> "My Computer" -> "Distributed Transaction Coordinator" -> "Local Service DTC"
y seleccionamos " Propiedades ".
Se debe marcar " Permitir cliente remoto ", " Permitir entrada ", " Permitir saliente " y " No se requiere autenticación ".
Mi problema fue estúpido, si te sientas en un corte de depuración durante el tiempo de espera obtendrás esto. Cara Palm
Hombre, la programación te hace sentir gruesa algunos días ...
No olvide suprimir sus declaraciones seleccionadas de su TransactionScope. En SQL Server 2005 y versiones posteriores, incluso cuando usa con (nolock), los bloqueos aún se crean en esas tablas que toca la selección. Verifique esto, le muestra cómo configurar y usar TransactionScope .
using(TransactionScope ts = new TransactionScope
{
// db calls here are in the transaction
using(TransactionScope tsSuppressed = new TransactionScope (TransactionScopeOption.Suppress))
{
// all db calls here are now not in the transaction
}
}
Puedo reproducir el problema Es un tiempo de espera de transacción.
using (new TransactionScope(TransactionScopeOption.Required, new TimeSpan(0, 0, 0, 1)))
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
using (var sqlCommand = connection.CreateCommand())
{
for (int i = 0; i < 10000; i++)
{
sqlCommand.CommandText = "select * from actor";
using (var sqlDataReader = sqlCommand.ExecuteReader())
{
while (sqlDataReader.Read())
{
}
}
}
}
}
}
Lanza System.InvalidOperationException con este mensaje: "La transacción asociada con la conexión actual se ha completado pero no se ha eliminado. La transacción debe eliminarse antes de que la conexión se pueda usar para ejecutar sentencias SQL".
Para resolver el problema, haz que la consulta se ejecute más rápido o aumenta el tiempo de espera.
Si ocurre una excepción dentro de un TransactionScope
, se revierte. Esto significa que TransactionScope
está hecho. Ahora debe llamar a dispose()
y comenzar una nueva transacción. Honestamente, no estoy seguro si puede reutilizar el viejo TransactionScope
o no, nunca lo intenté, pero supongo que no.