Dynamiczne tworzenie zapytań do Entity Framework

By | June 22, 2016

Czasem może nas spotkać sytuacja, że bardzo potrzebujemy stworzyć zapytanie do Entity Framework, które musi dynamicznie reagować na nasze wymagania i w jednym konkretnym przypadku będzie potrzebowało dodatkowego Where(). Oczywiście, możemy napisać oddzielne zapytania i warunek, ale istnieje odrobinę sprytniejszy sposób.

Zacznijmy od nakreślenia prostego kontekstu sytuacji. Załóżmy, że coś sprzedajemy i na przykład w poniedziałki nie chcemy, by była widoczna część naszej oferty. Nie ma to najmniejszego sensu i akurat ten przykład da się rozwiązać w nieco inny i lepszy sposób, ale do zobrazowania idei jak najbardziej wystarczy.

Standardowym zapytaniem zwracającym produkty w te piękniejsze dni tygodnia byłoby poniższe kilka linijek opakowane w jakąś metodę:

Te samo zapytanie wykonywane w poniedziałek wyglądałoby następująco:

Co możemy z tym zrobić? Najprostsze rozwiązanie przychodzi mi do głowy niemalże od razu:

Nie chodzi nam jednak o to, bo o ile w tym przypadku jest to proste, możemy spotkać się z inną sytuacją. Sęk w tym, że naszym zamiarem jest umieszczenie w zapytaniu lub usunięcie z niego tego jednego Where(), w zależności od tego jaki mamy dzień tygodnia. W tym celu możemy wykorzystać to jak zachowuje się LINQ, a w zasadzie to cokolwiek co korzysta z Fluent API, o którym pisałem kiedyś w tym poście. Chodzi o to, że przy łączeniu metod w łańcuchy, zwracany jest nam cały czas jeden i ten sam obiekt. Możemy więc pokusić się o taką konstrukcję, którą celowo rozdrobniłem na najdrobniejsze etapy, w których budowane jest całe zapytanie:

Takich warunków możemy mieć bardzo dużo i dzięki temu możemy dosyć dynamicznie dodawać lub usuwać fragmenty zapytania, do Entity Framework. Najfajniejsze w tym wszystkim jest to, że do momentu wywołania metody .ToList() operujemy na IQueryable, więc nasz ORM skonstruuje nam jedno zapytanie do bazy.

A co jeżeli mamy różne dziwne kaprysy w każdym możliwym dniu tygodnia? I nie chcemy robić bajzlu w kodzie, a koniecznie chcemy mieć ustawione 7 warunków na każdy pojedynczy dzień? Oczywiście moglibyśmy rzucić jakimś switchem i zupełnie nie przejmować się tym jak to wygląda. Moglibyśmy też całość opakować w jakąś ładną metodę rozszerzającą. Na przykład taką:

A wykorzystanie jej w kodzie mogłoby wyglądać następująco:

Mam nadzieję, że tym prostym przykładem udało mi się w miarę treściwie pokazać, jak można modyfikować zapytanie w trakcie jego budowania.

PS. Fajnie jest wrócić do technicznych treści 😉

EDIT: Na twitterze wywiązała się bardzo fajna dyskusja i faktycznie zapomniałem wspomnieć o tym, że typ IQueryable wymaga ostrożności i generalnie nie powinien być wyprowadzany poza repozytorium/inną warstwę dostępu do danych. Więcej w poście Jakuba Skoczenia tutaj.

EDIT2: Na blogu Pasja Programowania pojawiła się bardzo ciekawa odpowiedź na powyższego posta z nieco ładniejszym rozwiązaniem. Zachęcam do przeczytania tutaj.