ejemplos await async asincronos acciones c# .net async-await transactionscope

await - asincronos c#



Asincrónicamente confirmar o revertir un ámbito de transacción (1)

Tal vez una respuesta tardía, pero lo que quieres básicamente se reduce a una especie de azúcar sintáctica que puedes crear fácilmente por tu cuenta.

Generalizando su problema, implementé una sintaxis de "async using", que permite que tanto el cuerpo como la parte de "disponer" del "uso" sean esperados. Aquí está cómo se ve:

async Task DoSomething() { await UsingAsync.Do( // this is the disposable usually passed to using(...) new TransactionScope(TransactionScopeAsyncFlowOption.Enabled), // this is the body of the using() {...} async transaction => { await Task.Delay(100); // do any async stuff here... transaction.Complete(); // mark transaction for Commit } // <-- the "dispose" part is also awaitable ); }

La implementación es tan simple como esto:

public static class UsingAsync { public static async Task Do<TDisposable>( TDisposable disposable, Func<TDisposable, Task> body) where TDisposable : IDisposable { try { await body(disposable); } finally { if (disposable != null) { await Task.Run(() => disposable.Dispose()); } } } }

Hay una diferencia en el manejo de errores, en comparación con la cláusula de using regular. Al usar UsingAsync.Do , cualquier excepción lanzada por el cuerpo o la disposición se envolverá dentro de una excepción AggregateException . Esto es útil cuando tanto el cuerpo como la disposición de cada uno lanzan una excepción, y ambas excepciones se pueden examinar en una excepción AggregateException . Con la cláusula de using regular, solo se capturará la excepción lanzada por disposición, a menos que el cuerpo esté explícitamente envuelto en try..catch .

Como muchos saben, TransactionScope se olvidó cuando se introdujo el patrón de await async en .Net. Se rompieron si intentáramos usar alguna llamada de await dentro de un ámbito de transacción.

Ahora esto está arreglado gracias a una opción de constructor de alcance .

Pero me parece que aún falta una pieza, al menos no puedo encontrar la forma de hacerlo de una manera simple "de alcance de transacción": ¿cómo esperar el compromiso o la reversión de un alcance?

El compromiso y la reversión también son operaciones de E / S, deben estar pendientes. Pero como se trata de la eliminación del alcance, tendríamos que esperar la disposición. Eso no parece factible (ni práctico con el patrón de using ).

También eché un vistazo a la interfaz System.Transactions.Transaction : tampoco hay métodos que se puedan esperar.

Entiendo que cometer y revertir es casi solo enviar una bandera a la base de datos, por lo que debería ser rápido. Pero con las transacciones distribuidas, eso podría ser menos rápido. Y de todos modos, eso sigue siendo un bloqueo de IO.

Acerca de los casos distribuidos, recuerde que esto puede desencadenar un compromiso de dos fases. En algunos casos, los recursos duraderos adicionales se enlistan durante la primera fase (preparación). Por lo general, entonces significa que se emiten algunas consultas adicionales contra los recursos recientemente enlistados. Todo lo que sucede durante el compromiso.

Entonces, ¿hay alguna manera de esperar un alcance de transacción? O un System.Transactions.Transaction lugar?

Nota: No considero que esto sea un duplicado de " ¿Es posible confirmar / revertir SqlTransaction en asíncrono? ". SqlTransaction son más limitadas que las transacciones del sistema. Pueden dirigirse solo al servidor SQL y nunca se distribuyen. Algunas otras transacciones tienen métodos asíncronos, como Npgsql . Ahora, para tener métodos asíncronos en los ámbitos de transacción / transacción del sistema, puede ser necesario que DbTransaction tenga métodos asíncronos. (No conozco los aspectos internos de la transacción del sistema, pero es posible que esté utilizando este contrato de ADO.NET. La forma en que incorporamos la conexión en la transacción del sistema me permite pensar que no lo usa).