W mojej aplikacji .NET Core, mam klasę dekoratorów, które miałem nadzieję, że będą w stanie obsłużyć transakcje poprzez zawijanie wykonywania komend bazy danych w TransactionScope. Niestety, wydaje się, że wsparcie dla TransactionScope nie wejdzie w SqlConnection przez wydanie .NET Core 2: https://github.com/dotnet/corefx/issues/19708:.NET Renderowanie transakcji Core bez TransactionScope
W przypadku braku TransactionScope, nie jestem pewien najlepszego podejścia do tego problemu . Z TransactionScope, mój dekorator transakcja wygląda następująco:
public class TransactionCommandHandlerDecorator<TCommand> : ICommandHandler<TCommand>
{
private readonly ICommandHandler<TCommand> decorated;
//constructor
public void Handle(TCommand command)
{
using (var scope = new TransactionScope())
{
this.decorated.Handle(command);
scope.Complete();
}
}
}
Obecnie każda realizacja ICommandHandler dostaje wystąpienie z mojej klasy DapperContext i obsługuje polecenia tak:
public void Handle(UpdateEntity command)
{
var sql = Resources.UpdateEntityPart1;
this.context.Execute(sql, new
{
id = command.Id;
});
var sql = Resources.UpdateEntityPart2;
//call Execute again
}
Klasa DapperContext ma fabrykę połączeń aby zapewnić nowe połączenia dla każdego wywołania metody Execute. Ponieważ program obsługi komend może być zmuszony do wykonywania wielu zapisów do bazy danych dla pojedynczego TCommand, potrzebuję możliwości wycofania, gdy coś się nie powiedzie. Konieczność tworzenia transakcji w tym samym czasie, w którym tworzę połączenia (w DapperContext) oznacza, że nie mam możliwości zagwarantowania zachowania transakcyjnego między połączeniami.
Ten alternatywny mam uznać nie wydaje się, że wszystko satysfakcjonujące:
- Zarządzaj połączeniami i transakcji na poziomie procedury obsługi poleceń, a następnie przekazać te informacje do Wytworny kontekście. W ten sposób wszystkie zapytania dla danej komendy używają tego samego połączenia i transakcji. To może zadziałać, ale nie podoba mi się pomysł obciążania moich komendantów tą odpowiedzialnością. Pod względem ogólnego projektu wydaje się bardziej naturalne, że DapperContext jest miejscem, w którym trzeba się martwić o połączenie.
Moje pytanie brzmi: czy istnieje sposób na zapisanie dekoratora transakcji bez użycia TransactionScope, biorąc pod uwagę obecne ograniczenia SqlConnection w .NET Core? Jeśli nie, to jakie jest najlepsze rozwiązanie, które nie rażąco narusza zasadę pojedynczej odpowiedzialności?
Gdybym był tobą, skomentowałbym ten konkretny problem z Girhubem, by wyjaśnić Microsoftowi, że jest to poważna niezgodność i absolutnie należy to naprawić. Myślę, że jest to poważny problem dla wielu organizacji migrujących, jeśli TransactionScope nie działa w Core 2. – Steven