2012-03-12 13 views
5

Mam następujący LINQ podmiotom kwerendy, która ma wiele podzapytania trochę zbiorczych danych:Refactor LINQ to SQL/podmioty kwerendy z wieloma podkwerend

var systems = from s in db.Systems 
       orderby s.Name 
       select new SystemSummary 
       { 
        Id = s.Id, 
        Code = s.Code, 
        Name = s.Name, 
        LastException = (
         from a in s.Applications 
         from e in a.Summaries 
         select e.CreationDate 
       ).Max(), 
        TodaysExceptions = (
         from a in s.Applications 
         from e in a.Summaries 
         where e.CreationDate >= today && e.CreationDate < tomorrow 
         select e 
       ).Count(), 
        /* SNIP - 10-15 more subqueries */        
       }; 

I skróconą się zapytanie tylko to 2 z podzapytania , ale może być ich około 10-15. Czy istnieje sposób, w jaki mogę refaktoryzować zapytanie, aby oczyścić kod? Nie szukam wzrostu wydajności. Chcę po prostu posprzątać kod przez wprowadzenie podkwerendy w osobne metody przy jednoczesnym upewnieniu się, że jest to pojedyncze połączenie z bazą danych. Czy to możliwe?

+0

Jeśli potrzebujesz czystszego kodu, możesz rozważyć utworzenie procedury przechowywanej w bazie danych. – Mathieu

+0

@Mathieu Czy to jednak jedyny sposób? – Dismissile

Odpowiedz

2

Właśnie może zaoferować zminimalizować jego długość o czymś takim (przy użyciu let kluczowe w pierwotnym zapytaniu):

var subQuery = from a in s.Applications 
        from e in a.Summaries 
        select e; 

Ponadto można mieć pewne refactors jak:

subQuery.Count(e=>e.CreationDate >= today && e.CreationDate < tomorrow); 

subQuery.max(e=>e.CreationDate); 

W rzeczywistości użyj notacji kropkowej i przenieś zapytanie do powiązanej funkcji zamiast dodatkowej klauzuli where.

i używać subQuery w zapytaniu:

  from s in db.Systems 
      orderby s.Name 
      let subQuery = from a in s.Applications 
        from e in a.Summaries 
        select e 
      select new SystemSummary 
      { 
       Id = s.Id, 
       Code = s.Code, 
       Name = s.Name, 
       LastException = subQuery.max(e=>e.CreationDate), 
       TodaysExceptions = subQuery.Count(e=>e.CreationDate >= today 
              && e.CreationDate < tomorrow), 
       /* SNIP - 10-15 more subqueries */        
      }; 

Jest jeszcze jedno wezwanie do DB.

+0

@ Dismissile, Tęskniłem 's', redagowałem odpowiedź, możesz użyć' let', aby zasymulować ten sposób w zapytaniu. –

+0

Podoba mi się to podejście. – Dismissile

+0

Mam nadzieję, że ta pomoc, też miło widać komentarz :) –

0

Naprawdę nie ma problemu z dzieleniem zapytania na wiele metod. Są jednak pewne warunki.

Upewnij się, że zapytanie jest niezliczone. Jest to domyślnie.

Funkcja IEnumerable zapewnia, że ​​zapytanie jest przechowywane w zmiennej, ale nie jest wykonywane. Kompilator optymalizuje zapytania w czasie wykonywania.

szybki i brudny exemple:

private MyContext context = new MyContext() 
private IEnumerable<User> getUser(Guid userID) 
{ 
    return context.User.Where(c => c.ID == userID); 
} 

private void evaluateUser() 
{ 
    bool isUserActive getUser().Any(c => c.IsActive) 
} 

Widać, że zapytanie jest w dwóch metod. Wciąż istnieje tylko jedno połączenie do DB, ponieważ IEnumerable przechowuje zapytanie, a nie wynik. Zapytanie jest wykonywane tylko w razie potrzeby.

+2

Myślę, że masz na myśli IQueryable, a nie IElumerable.IEnumerable sprawi, że przekształci zapytanie w obiekt w pamięci. –

0

Możesz rozważyć użycie słowa kluczowego let, aby utworzyć "zmienne" lokalne dla zapytania (ostatecznie będzie to podkwerenda). Na przykład:

var systems = from s in db.Systems 
       orderby s.Name 
       let lastException = (from a in s.Applications from e in a.Summaries select e.CreationDate).Max() 
       ... 

Inną opcją może zrobić, to być może tworzyć podzapytania z różnych stowarzyszeń tuż nietoperza, i pracować z tymi elementami.

var systems = from s in db.Systems 
       orderby s.Name 
       from summaries in 
        (from ta in s.Applications 
        from te in ta.Summaries 
        ... 
        select { APPS = ta, SUMMS = te ,/*anything else you want*/ }) 
       let lastExpire = (from summaries select SUMMS.CreationDate).Max() 

Cholera, nawet można po prostu zostawić niech w drugim przykładzie i po prostu korzystać z summaries podmioty w swoich końcowych wybiera. Możliwe, że będziesz musiał się z nim trochę pogodzić, aby upewnić się, że nie uzyskasz żadnych duplikatów wartości, ale przynajmniej w ten sposób możesz po prostu dokonać prostej selekcji względem swojej summaries zamiast przepisywania podkwerend za każdym razem.