2015-07-02 15 views
7

użyciu LINQ-SQL, zastanawiam się, co jest bardziej idiomatyczne następnego trzech,Czy sprawdzanie wartości null w zapytaniach Linq jest idiomatyczne?

  • foos.Where(foo => foo.Bar.HasValue && foo.Bar.Value < 42)
  • foos.Where(foo => foo.Bar.Value < 42)
  • foos.Where(foo => foo.Bar < 42)

Pierwsza opcja generuje dodatkowe Bar IS NOT NULL predykat, który prawdopodobnie jest zoptymalizowany w większości systemów DBMS. Jeśli jeden obiekt zapytał zamiast bazy danych, kontrola zerowa byłaby obowiązkowa, ale ponieważ można generować kwerendy generyczne, które mogłyby zawieść w obiektach, ale nie w bazach danych, pierwsza opcja zawsze działałaby, chociaż zarówno kod Linq i SQL jest trochę dłużej niż druga opcja. Trzecia opcja, dostarczona przez Michael Liu, wydaje się najlepsza z obu światów, ale nie zadziała w przypadku foo.Bar ma bool typu ?: foos.Where(foo => foo.Bar) (powoduje błąd typu, ponieważ nie jest tutaj dokonywana niejawna konwersja).

Czy należy dążyć do napisania ogólnych zapytań, które się nie zawiedą, jeśli są używane poza kontekstem, dla którego zostały pierwotnie zaprojektowane?

Odpowiedz

2

Trzecią opcją jest użycie operatora "podniesiony" <, który zwraca wartość false, jeśli dowolny argument jest zerowy. To pozwala pominąć wyraźny czek za nieważną i działa na wszystkich dostawców LINQ:

foos.Where(foo => foo.Bar < 42) 

Jeśli typ kompilacji czas foos jest IEnumerable<Foo>, następnie foo.Bar < 42 jest równoważna

foo.Bar.GetValueOrDefault() < 42 && foo.Bar.HasValue 

chyba że foo.Bar jest dostęp tylko raz. (Mam to przez dekompilacji programu w reflektor. To ciekawe, że kompilator zdecyduje się wykonać porównanie przed sprawdzanie null, ale ma to sens jako mikro optymalizacji.)

Jeśli typ kompilacji czas foos jest IQueryable<Foo>, następnie foo.Bar < 42 jest równoważna

foo.Bar < (int?)42 

i to w gestii dostawcy LINQ, czy i jak sprawdzić dla wartości null.


UPDATE: Jeśli typ foo.Bar jest bool? a ty tylko chcesz dołączyć rekordy gdzie foo.Bar jest prawdziwe (czyli ani fałszywe ani null), to można napisać

foos.Where(foo => foo.Bar == true) 

lub

foos.Where(foo => foo.Bar.GetValueOrDefault()) 
+0

Czy możesz pokazać przykładową implementację _wzmocnionego_ '' '? Mam problem z próbą zrozumienia, jak to będzie wiedzieć, czy jest to 'IQueryable' lub' IEnumerable' – Aducci

+0

@Aducci: Kompilator traktuje wyrażenie lambda jako jedno lub drugie w oparciu o typ kompilacji 'foos'. –

+0

Operator zobaczy tylko 'foo.Bar' i' 42', z których żaden nie jest 'IQueryable' ani' IEnumerable'. Dlaczego miałby znać rodzaj "foos" w czasie kompilacji? – Aducci

4

Podstawowa technologia przechowywania danych może się zmieniać na przestrzeni lat. Kod wydaje się żyć o wiele dłużej niż ktokolwiek mógłby sądzić, że kiedykolwiek będzie. Nie bazowałbym na założeniach dotyczących dostawcy pamięci masowej.

Jeśli Linq jest twoją warstwą abstrakcji, załóż, że dostawca Linq może być podłączony w pewnym momencie cyklu życia aplikacji.

Pierwszy z nich generuje dodatkowy wskaźnik Bar NIE JEST NULL, który prawdopodobnie jest zoptymalizowany w większości systemów DBMS.

Nie jestem ekspertem od wydajności baz danych, ale uważam, że koszt wydajności dodatkowej opcji NIE JEST NULL będzie niewielki w porównaniu do ogólnego kosztu wielu zapytań.

+0

Zgadzam się - Nie pracuję z wyliczeniami Linq podczas zwracania wyników z poziomów danych, pracuję z listą X –

2

Zapisałbym zapytania specyficzne dla dostawcy, z którego korzystasz. Linq-to-objects może wyglądać podobnie do linq-to-sql/entities, ale zachowują się one bardzo różnie.

  1. one nie obsługują te same funkcje
  2. Oni nie zwracają ten sam typ
  3. są realizowane w różny sposób

Jeśli LINQ zmiany dostawcy w przyszłości, to będzie najbardziej prawdopodobnie i tak trzeba będzie napisać ponownie swoje zapytania. Na przykład istnieją specyficzne dla dostawcy klasy, takie jak SqlFunctions, które działają tylko na danym dostawcy. Nie można przewidzieć, co będzie wspierać przyszły dostawca i jak to będzie działać.

Prosty grupa przez kwerendę w LINQ-podmioty

var query = from f in foos 
      group f by new { f.Name, f.Bar.Value } into g 
      select ... 

skutkowałoby znacznie differnt linq-to-objects zapytania. Czy korzystne jest znalezienie zapytania, które zadowoli oba?

0

Translator Linq-SQL będzie odpowiedzialny za wyprowadzenie poprawnego kodu SQL. W tym przypadku twój pseudo ORM kod odzwierciedla kolumnę prętów zerowalnych. Jest to użyteczne w kodzie aplikacji, podczas pracy z zwróconymi wynikami, ale nie tak bardzo dla klauzuli where zapytania MS SQL, jak w twoim przykładzie. Zostawiłbym to w twojej drugiej opcji.