Gran parte de nuestro código DAL usa TransactionScope para transacciones. Eso funciona bien, pero hay un problema cuando uso este código DAL desde dentro de un procedimiento SQLCLR. La Transacción pasa a MSDTC lo que no quiero.Cómo utilizar TransactionScope en SQLCLR sin escalación a MSDTC
El problema se puede reproducir fácilmente:
CLR Implementación
[SqlProcedure] public static void ClrWithScope(string cmdText) { /* escalates to MSDTC when a transaction is already open */ using (var scope = new TransactionScope()) { using (var connection = new SqlConnection("context connection=true;")) { connection.Open(); using (var cmd = new SqlCommand(cmdText, connection)) { SqlContext.Pipe.ExecuteAndSend(cmd); } } scope.Complete(); } } [SqlProcedure] public static void ClrWithTrans(string cmdText) { /* works as expected (without MSDTC escalation) */ using (var connection = new SqlConnection("context connection=true;")) { connection.Open(); using (var tx = connection.BeginTransaction()) { using (var cmd = new SqlCommand(cmdText, connection, tx)) { SqlContext.Pipe.ExecuteAndSend(cmd); tx.Commit(); } } } }
secuencia de comandos SQL se utiliza para ejecutar el procedimiento CLR
BEGIN TRANSACTION exec dbo.ClrWithTrans "select * from sys.tables"; exec dbo.ClrWithScope "select * from sys.tables"; /* <- DOES NOT WORK! */ ROLLBACK TRANSACTION
el error
Msg 6549, Level 16, State 1, Procedure ClrWithScope, Line 0 A .NET Framework error occurred during execution of user defined routine or aggregate 'clrClrWithScope': System.Transactions.TransactionAbortedException: Die Transaktion wurde abgebrochen. ---> System.Transactions.TransactionPromotionException: MSDTC on server 'BLABLA' is unavailable. ---> System.Data.SqlClient.SqlException: MSDTC on server 'BLABLA' is unavailable. System.Data.SqlClient.SqlException: bei System.Data.SqlServer.Internal.StandardEventSink.HandleErrors() bei System.Data.SqlServer.Internal.ClrLevelContext.SuperiorTransaction.Promote() System.Transactions.TransactionPromotionException: bei System.Data.SqlServer.Internal.ClrLevelContext.SuperiorTransaction.Promote() bei System.Transactions.TransactionStatePSPEOperation.PSPEPromote(InternalTransaction tx) bei System.Transactions.TransactionStateDelegatedBase.EnterState(InternalTransaction tx) System.Transactions.TransactionAbortedException: bei System.Transactions.TransactionStateAborted.CreateAbortingClone(InternalTransaction tx) bei System.Transactions.DependentTransaction..ctor(IsolationLevel isoLevel, InternalTransaction internalTransaction, Boolean blocking) bei System.Transactions.Transaction.DependentClone(DependentCloneOption cloneOption) bei System.Transactions.TransactionScope.SetCurrent(Transaction newCurrent) bei System.Transactions.TransactionScope.PushScope() bei System.Transactions.TransactionScope..ctor(TransactionScopeOption scopeOption) bei Giag.Silo.Data.SqlClr.ClrWithScope(String cmdText) . User transaction, if any, will be rolled back.
wihtout la "BEGIN TRANSACTION" declaración, la llamada dbo.ClrWithScope funciona bien. Supongo que la transacción iniciada por SQLServer no se considera al alistarse en .Net Framework.
¿Hay alguna solución para solucionar esto? Una idea es crear manualmente un SqlTransaction y hacer que TransactionScope use esta transacción, pero no sé cómo hacerlo. Otra solución sería hacer un caso especial en todo el código DAL (no es realmente divertido de implementar).
¿Alguna idea?
trate de colocar el '' Connection' alrededor de los Scope' – Magnus
@Magnus Pero eso no significa (automáticamente) Contar con la conexión en la transacción ambiente, lo que es todo el propósito de la TransactionScope, ¿no es así? –
Christian.K tiene razón. El código DAL usa mucho TransactionScope y SqlConnection. Por supuesto, es posible reescribir toda la base de código en DAL, pero no es agradable. – sschoenb