2012-02-03 15 views
10

Używam linq do entity (EF). Mam konstruktora, który ma 4 parametry ciąg znaków. W zależności od tego, który parametr nie jest pusty, muszę zbudować zapytanie linq. Mogę zrobić z instrukcjami else, ale mam też inny konstruktor z 10 parametrami, w takim przypadku będzie wiele kombinacji do sprawdzenia.Dynamiczne gdzie klauzula w Linq do Entity

przykład:

Constructor(p1,p2,p3,p4) 
{ 
    var prod= from p in ctxt.products.expand("items\details") 
      where p.x==p1 && p.xx==p2 && p.xxx==p3 && p.xxxx==p4 
      select p; 
} 

W powyższym przykładzie, gdy warunek nie powinno być tylko sprawdza warunek, że parametr ten nie jest zerowy. tzn. jeśli p2 jest null wtedy, gdy klauzula powinna wyglądać

where p.x==p1 && p.xxx==p3 && p.xxxx==p4 

jeśli P2 i P3 są nieważne następnie

where p.x==p1 && p.xxxx==p4 

Czy ktoś może mi powiedzieć, jak sobie z tym poradzić. jeśli to możliwe, można podać przykładowy kod dla tej

+0

Możliwy duplikat http://stackoverflow.com/questions/697345/linq-to-sql- need-dynamic-where-klauzula-over-relational-tables-help? rq = 1 –

Odpowiedz

11

Odroczona egzekucja Linqa na ratunek. Zapytanie Linq nie jest wykonywane, chyba że zażądano od niego danych.

var prod = from p in ctxt.products.expand("items\details") 
     select p; 

if (p1 != null) 
{ 
    prod = prod.Where(p => p.x == p1); 
} 

if (p2 != null) 
{ 
    prod = prod.Where(p => p.xx == p2); 
} 

// Execute the query 

var prodResult = prod.ToList(); 
+0

jeśli to zrobimy, będziemy ładować dużą ilość danych w prod, a następnie biorą podzbiór tego. Czy możemy uzyskać podzbiór bezpośrednio. – Deepak

+3

Kwerenda ** NOT ** jest wykonywana do momentu zbudowania klauzuli where. W powyższym kodzie zapytanie jest budowane do ostatniej linii i tam jest wykonywane. Możesz to zrobić, uruchamiając profiler SQL. –

+0

Tak, masz rację. Mogę użyć twojego kodu i jego idealnie działającego. – Deepak

3

Można łańcuch metody jak potrzeba:

YourType(string p1, string p2, string p3, string p4) 
{ 
     var prod = ctxt.Products.Expand("items\details"); 

     if (!p1.IsNullOrWhiteSpace()) 
      prod = prod.Where(p => p.x == p1); 
     if (!p2.IsNullOrWhiteSpace()) 
      prod = prod.Where(p => p.xx == p2); 

     // .... 

     // use "prod" 
} 

Powstały SQL powinna być taka sama, jak gdyby umieścić je wszystkie w jednym rachunku.

+1

jeśli to zrobimy, to będziemy ładować dużą ilość danych w prod, a następnie robimy podzbiór. Czy możemy uzyskać podzbiór bezpośrednio inaczej będziemy ładować niepotrzebne dane – Deepak

+1

@Deepak Nie, nie będzie. Zaletą IQueryable jest to, że nie "ładuje danych" - LINQ wykorzystuje odroczoną realizację, a przy EF, tłumaczy końcowe "zapytanie" na pojedynczą instrukcję SQL. To będzie ten sam wynik. –

+1

@Deepak Aby uzyskać szczegółowe informacje, zobacz: http://stackoverflow.com/questions/1578778/using-iqueryable-with-linq/1578977#1578977 –

2

Zawsze można zbudować kwerendę w kawałki i wykorzystać opóźnionego wykonania kwerendy:

public Constructor(int? p1, int? p2, int? p3, int? p4) 
{ 
    var prod = ctxt.products.expand("items\details"); 

    if(p1 != null) 
     prod = prod.Where(p.x == p1); 

    if(p2 != null) 
     prod = prod.Where(p.xx == p2); 

    if(p3 != null) 
     prod = prod.Where(p.xxx == p3); 

    if(p4 != null) 
     prod = prod.Where(p.xxxx == p4); 
} 
+0

jeśli to zrobimy, będziemy ładować dużą ilość danych w prod, a następnie będziemy robić podzbiór. Czy możemy uzyskać podzbiór bezpośrednio, w przeciwnym razie ładujemy niepotrzebne dane – Deepak

+1

@Deepak - Nie będziesz ładować żadnych danych w ogóle. LINQ nie wykonuje zapytania, dopóki faktycznie nie użyjesz danych. Do czasu wykonania kwerendy, masz już odpowiednie klauzule, gdzie można załadować tylko potrzebne dane. –