2010-12-29 15 views
26

Gram z FluentNHibernate i NH 3.0, korzystając z dostawcy LINQ i nowej składni QueryOver.NHibernate 3.0: Nie FirstOrDefault() z QueryOver?

Teraz QueryOver Chcę dostać element (zwany wynik) o wartości znacznika czasu jak najbliżej do danej wartości, ale nie wyższa:

Result precedingOrMatchingResult = Session.QueryOver<Result>(). 
     Where(r => r.TimeStamp < timeStamp). 
     OrderBy(r => r.TimeStamp).Desc.     
     FirstOrDefault(); //get the preceding or matching result, if there is any 

Teraz Intellisense mówi mi, że nie ma coś takiego jak metoda FirstOrDefault(). Mogę, oczywiście, wyliczyć moje zamówione zapytanie, a następnie użyć LINQ, aby uzyskać mój przedmiot. Ale najpierw załadowałoby wszystkie elementy do pamięci.

Czy istnieje alternatywa dla FirstOrDefault(), czy też zrozumiałem coś zupełnie nie tak?

+0

Poszukaj 'SingleOrDefault()'. Proszę przyjąć odpowiedź @RRR. –

Odpowiedz

10

NH 3 posiada zintegrowany dostawcy LINQ (zapytania są tłumaczone wewnętrznie na HQL/SQL). Trzeba dodać nazw NHibernate.Linq a następnie:

Result precedingOrMatchingResult = Session.Query<Result>(). 
    Where(r => r.TimeStamp < timeStamp). 
    OrderByDescending(r => r.TimeStamp). 
    FirstOrDefault(); 
+0

Wygląda wspaniale, spróbuję (tylko w przyszłym roku, odkąd jestem teraz wolny!) – Marcel

+0

To działa. Jednak ponieważ w moich klauzulach Where istnieją pewne dodatkowe ograniczenia, stwierdziłem, że LINQ do NH nie obsługuje obsługi metody .HasValue() dla typów zerowalnych. Szkoda, ale teraz czekam na! = Null, który działa. – Marcel

+8

Nie odpowiada to na pytanie o to za pomocą QueryOver . Odpowiedź jest ważna właśnie dla zadanego pytania. @ RRR's jest bardziej poprawny. – Jafin

9

Spróbuj

Result precedingOrMatchingResult = Session.QueryOver<Result>(). 
     Where(r => r.TimeStamp < timeStamp). 
     OrderBy(r => r.TimeStamp).Desc. 
     SetFetchSize(1). 
     UniqueResult(); 

UniqueResult powróci pojedynczą wartość lub wartość null, jeśli nie zostanie znaleziony, który jest trochę co pierwsze lub domyślne robi.

Ustawienie wielkości pobierania na 1 może być wymagane, ale przetestuję to za pomocą profilera.

+0

Spojrzałem na to. UniqueResult wydaje się być podobny do SingleOrDefault, ale nie mam jednego elementu. Mam listę i chcę wybrać tylko pierwszy przedmiot. – Marcel

+0

To, co ma zwrócić zapytanie, jest pierwszym elementem, prawda? Dlaczego warto zwrócić listę z serwera bazy danych, a następnie wyrzucić większość pracy? –

35

Mam teraz okazało się, że mogę korzystać z Take() metodę rozszerzenia na przykład IQueryOver i tylko enumerate do listy, tak jak poniżej:

Result precedingOrMatchingResult = Session.QueryOver<Result>(). 
     Where(r => r.TimeStamp < timeStamp). 
     OrderBy(r => r.TimeStamp).Desc. 
     Take(1).List(). //enumerate only on element of the sequence! 
     FirstOrDefault(); //get the preceding or matching result, if there is any 
+6

Yup. 'Take' jest tym, co chciałem polecić. Połącz to z RRR 'SingleOrDefault' zamiast' List(). FirstOrDefault() 'i jest idealne. –

+0

Właściwie '.Take (1)' zrobi to samo, co '.SetFetchSize (1)' w tym sensie, że powie SQLowi, że chcesz tylko jeden wynik. W obu przypadkach nie wykonujesz zapytania dla wszystkich pasujących wyników, a następnie wyliczasz na podstawie pierwszego. –

22
Result precedingOrMatchingResult = Session.QueryOver<Result>() 
              .Where(r => r.TimeStamp < timeStamp) 
              .OrderBy(r => r.TimeStamp).Desc 
              .SingleOrDefault(); 
+1

Tak, to jest poprawna odpowiedź. 'SingleOrDefault()' –

+3

Powoduje NHibernate.NonUniqueResultException – Graham

+0

Otrzymuję wyjątek wyjątkowy wyjątek zbyt. –

Powiązane problemy