ODP.NET, którego używamy do budowania dużego projektu internetowego wprowadził nas w prawdziwe zakłopotanie a my straciliśmy dwa dni na poszukiwania czegoś co w rzeczywistości nie ma prawa działać.
Artykuł ten można traktować jako rozszerzenie dla .NET Core - Sequential vs Async ponieważ tutaj został poruszony najbardziej trywialny przypadek. Teraz, pomijając warstwę WebAPI chcemy aby komunikacja z bazą danych odbywała się asynchronicznie. W poprzednim artykule wspomniałem o sterowniku bazy danych, który powinien udostępniać taką funkcjonalność. Wydawać by się mogło, że ODP.NET na to pozwala...
Od kiedy w języku C# oraz ASP.NET pojawiło się wsparcie dla async/await staram się zawsze wprowadzać to rozwiązanie ponieważ daje naprawdę duże korzyści związane ze skalowalnością – dodatkowo jest łatwe w implementacji. Pierwszą zmianą jaką zrobiłem było przerobienie wszystkich wywołań na asynchroniczne. W naszym kodzie znajduje się specjalna sekcja, która wymaga wywołania kilku procedur, których wykonanie nie jest zależne od siebie. Można wieć przypuszczać, że korzyść będzie tutaj znacząca.
Wszystko przebiegało płynnie do momentu pojawienia się poniższego błędu:
Połączenie dla tej operacji musi być otwarte
Nie będąc ekspertem w tej dziedzinie wciąż oczekiwałbym, że przy asynchronicznym wywołaniu każde wywołanie będzie miało swoje własne połączenie i błąd ten nie powinien się nigdy pojawić. A jednak, błąd taki został wielokrotnie zapisany w naszym dzienniku zdarzeń.
Przystąpiłem do poszukiwania informacji w internecie aż natrafiłem na ten post StackOverflow mówiący, że sterownik zarządzany przez Oracle nie wspiera operacji asynchronicznych.
Pomyślałem, że jest to nieprawda. Przecież posiadam dostęp do asnychronicznych metod! Nikt nie nazwałby metod async jeżeli w rzeczywistości nie byłby asynchroniczne!
W rzeczywistości Oracle Data Provider dla .NET udostępnia kilka asynchronicznych metod:
- ExecuteNonQueryAsync();
- ExecuteReaderAsync();
- ExecuteScalarAsync();
Z drugiej strony według Stackoverflow te metody nie są w rzeczywistości asynchroniczne. O co więc tak naprawdę chodzi?!
Przeglądając dokumentację ODP.NET nie można znaleźć żadnych odniesień do metod asynchronicznych, zadań Task czy Task<T> a nawet słowa kluczowego await. Nie jestem w ogóle znaleźć oficjalnej dokumentacji mówiącej, że wywołania asynchroniczne istnieją. Pojawia się pytanie: Dlaczego?!
Dokumentacja ze strony firmy Microsoft mówi, że:
In Visual Studio 2012 and the .NET Framework 4.5, any method that is attributed with the async keyword (Async in Visual Basic) is considered an asynchronous method...
Nigdy jednak nie jest powiedziane, że taka metoda musi być asynchroniczna! Zamiast tego znajdujemy jedynie informację, że metoda powinna zwracać Task lub Task<T> - a nawet to jest tylko konwencją!
Podsumowując powyższy przegląd informacji: Oracle nie zrobiło niczego złego (teoretycznie) poprzedzając nazwy tych metod słowem async ponieważ zwracają one Task lub Task<T>. W rzeczywistości te metody mogą być nawet użyteczne ponieważ mogą być asynchroniczne ale przy wykorzystaniu innego sterownika niż ten od Oracle. Nie takie jednak były nasze założenia.
Pojawia się kolejne pytanie:
Czy skoro te metody nie są wykonywane asynchronicznie jest sens ich zastosowania?
Krótka odpowiedź? NIE! Przynajmniej nie w powyższym scenariuszu.
Jedyne co mogę dodać w tym temacie to uzasadnienie nazewnictwa metod związane z istnieniem mistycznego interfejsu wspomnianego w odpowiedziach na Stackoverflow.
To rodzi kolejne pytanie:
Dlaczego interface potrzebuje deklaracji tych metod?
Ostatecznie, zanim zaszliśmy za daleko, zdecydowaliśmy się na usunięcie całego kodu asynchronicznego związanego z ODP.NET.
I tutaj pojawia się prośba do osób korzystających z ODP.NET. Jeżeli korzystasz z "metod asynchronicznych" i jesteś dokładnie zorientowany, proszę skontaktuj się ze mną - chciałbym dowiedzieć się w jakich sytuacjach użycie tych metod może być przydatne