web-dev-qa-db-fra.com

Validation ou restauration asynchrone d'une étendue de transaction

Comme beaucoup le savent, TransactionScope a été oublié lorsque le modèle asyncawait a été introduit dans .Net. Ils étaient cassés si nous essayions d'utiliser un appel await dans une portée de transaction.

Maintenant, cela est corrigé grâce à une option constructeur de portée .

Mais il me semble qu'il y a encore une pièce manquante, au moins je suis incapable de trouver comment faire cela d'une manière simple "portée de transaction comme": comment attendre la validation ou la restauration d'une portée?

La validation et la restauration sont des opérations IO également, elles devraient être attendues. Mais comme elles se produisent lors de la suppression de la portée, nous devons attendre la suppression. Cela ne semble pas faisable (ni pratique avec le using modèle).

J'ai regardé le System.Transactions.Transaction interface aussi: pas de méthodes attendues non plus.

Je comprends que la validation et la restauration sont presque simplement l'envoi d'un indicateur à la base de données, donc cela devrait être rapide. Mais avec des transactions distribuées, cela pourrait être moins rapide. Et de toute façon, c'est toujours un blocage d'E/S.

À propos des cas distribués, n'oubliez pas que cela peut déclencher une validation en deux phases. Dans certains cas, des ressources durables supplémentaires sont mobilisées au cours de la première phase (préparation). Cela signifie généralement que des requêtes supplémentaires sont émises contre ces ressources récemment enrôlées. Tout cela se passe pendant le commit.

Existe-t-il un moyen d'attendre une portée de transaction? Ou un System.Transactions.Transaction au lieu?

Remarque: Je ne considère pas qu'il s'agit d'un doublon de " Est-il possible de valider/annuler SqlTransaction en asynchrone? " . SqlTransaction sont plus limités que les transactions système. Ils ne peuvent adresser que SQL-Server et ne sont jamais distribués. Certaines autres transactions ont des méthodes asynchrones, telles que Npgsql . Maintenant, pour avoir des méthodes asynchrones sur les étendues de transaction/transaction système, DbTransaction peut être nécessaire d'avoir des méthodes asynchrones. (Je ne connais pas les aspects internes de la transaction système, mais il utilise peut-être ce contrat ADO.NET. La façon dont nous inscrivons la connexion dans la transaction système me laisse penser qu'il ne l'utilise pas cependant.)

45
Frédéric

Peut-être une réponse tardive, mais ce que vous voulez se résume essentiellement à une sorte de sucre syntaxique qui peut être facilement créé par vous-même.

En généralisant votre problème, j'ai implémenté une syntaxe "async using", qui permet d'attendre à la fois le corps et la partie "dispose" de "using". Voici à quoi ça ressemble:

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
    );
}

L'implémentation est aussi simple que cela:

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());
            }
        }
    }
}

Il existe une différence dans la gestion des erreurs par rapport à la clause régulière using. Lors de l'utilisation de UsingAsync.Do, toute exception levée par le corps ou la disposition sera enveloppée dans un AggregateException. Ceci est utile lorsque le corps et la disposition lèvent chacun une exception, et que les deux exceptions peuvent être examinées dans un AggregateException. Avec la clause régulière using, seule l'exception levée par dispose sera interceptée, sauf si le corps est explicitement enveloppé dans try..catch.

1
felix-b