Jak już wcześniej pisałem, swojego czasu pokazał się błąd w mojej aplikacji niezbyt wiele mówiący
Błąd ten pojawiał podczas wywołania zapytania LINQ. Najpierw myślałem, że coś jest nie tak z EntityFramework, którego wykorzystuję do połączenia z bazą danych. Trochę czasu i nerwów kosztowało mnie to aby dojść do wniosku, że to jednak nie jest wina Entity.
Winą nie na 100% ale coś w tej okolicy jest VisualStudio 2013 i ostatni update w wersji 3. Wiem, że jest już dostępna wersja 4 ale jest to dopiero CTP więc tego instalować nie będę (http://blogs.msdn.com/b/visualstudio/archive/2014/09/03/visua/l-studio-2013-update-4-ctp-1-ready-to-download.aspx). Skąd moje przypuszczenia? Ponieważ sytuacja pojawiała się tylko u mnie. Sprawdzenie tego samego u kolegi z biurka obok skutkowało tym, że wszystko odbywało się poprawnie. Do tego aplikacje uruchomiłem w VisualStudio 2012 i o dziwo też wszystko było w jak najlepszym porządku.
“Rozwiązanie” problemu nie rozwiązało niestety wszystkiego ponieważ przy wywoływaniu zapytania aplikacja czekała na jego wynik dobre 15 sekund a zapytanie samo w sobie wykonywało się na bazie w przeciągu 500 ms.
Sytuacja miała miejsce w przypadku filtrowania danych.
Magiczny kod, który powodował takiego laga wygląda następująco:
1 2 3 4 5 6 7 8 |
public IQueryable<vContract> GetContracts(int? contractId, int? mandatoryId, int? billId, int pageNumber, int skipCount) { return _connBase.Entities.fnContract(contractId, mandatoryId, billId) .OrderBy(e => e.ContractId) .Skip(skipCount) .Take(1000) .AsNoTracking(); } |
Jako, że aplikacja pracuje na dość dużej ilości danych wymogiem było to aby użytkownik dostawał tylko określony zestaw danych.
Zrobiliśmy stronicowanie danych, wszystko działało ładnie i szybko dopóki nie zaczęliśmy dodawać funkcjonalności filtrowania danych. Ale co może być nie tak przy dodaniu kolejnej, prostej funkcjonalności? No jak widać może.
Funkcja napisana w SQL przyjmuje kilka parametrów:
- identyfikator kontraktu
- identyfikator zleceniobiorcy
- identyfikator rachunku
Jeżeli parametry były przesłane jako null zwracała wszystko. W zależności od jakiegokolwiek parametru, brała go pod uwagę i na jego (ich) podstawie zwracała określony zestaw danych. I z tym właśnie był problem.
Jak zachowa się zapytanie, które ma zwrócić jeden rekord ale 1000 razy? No właśnie… chwilę to potrwa.
Błędem było wprowadzenie stałej ilości rekordów, które aplikacja chce uzyskać. W tym wypadku była to magiczna liczba 1000.
Dodanie jednego parametru do metody jakim był recorCount określający jak dużo rekordów zostanie zwróconych przez zapytanie przedstawia się następująco:
1 2 3 4 5 6 7 8 |
public IQueryable<vContract> GetContracts(int? contractId, int? mandatoryId, int? billId, int pageNumber, int skipCount, int recordCount) { return _connBase.Entities.fnContract(contractId, mandatoryId, billId) .OrderBy(e => e.ContractId) .Skip(skipCount) .Take(recordCount < 1000 ? recordCount : 1000) .AsNoTracking(); } |
Zmiana wywołania metody Take i dodanie parametru rozwiązało mój problem.
Wszystko da radę wytłumaczyć 😀
Dzięki za przydatne informacje . Trzeba dodać swój blog do zakładek