2012-12-19 9 views
6

Istnieje od 0 do n działów w mojej firmie, 0 do n biur w 1 dziale i 0 do n emplyees w 1 biurze. Teraz potrzebuję kwerendy przy użyciu linq, aby wyświetlić średni wiek pracowników dział, jeśli nikt w dziale następnie domyślny średnia wynosi 0. kod jest poniżej:Zewnętrzne zapytanie łączące za pomocą LINQ do jednostek

DataContext ctx = new DataContext(); 

    var q0 = from d in ctx.Departments 
      join o in ctx.Offices on d.Id equals o.DepartmentId 
      join e in ctx.Employees on o.Id equals e.OfficeId 
      group e by d into de 
      select new { 
       DepartmentId = de.Key.Id, 
       AverageAge = de.Count() == 0 ? 0 : de.Average(e => e.Age), 
      }; 


    var q1 = from d in ctx.Departments 
      join de in q0 on d.Id equals de.DepartmentId into des 
      from de in des.DefaultIfEmpty() 
      select new 
      { 
       DepartmentName = d.Name, 
       AverageAge = de == null ? 0 : de.AverageAge 
      }; 

    var result = q1.ToList(); 
    foreach (var item in result) 
    { 
     Console.WriteLine("{0}-{1}", item.DepartmentName, item.AverageAge); 
    } 
    ctx.Dispose(); 

Ale jak połączyć Q0 i Q1 do jednego zapytania?

+2

Oto dobry artykuł na temat tworzenia łączy się z LINQ: http://www.codeproject.com/Articles/488643/LinQ-Extended-Joins –

+0

Idealne rozszerzenie. Ale są to metody, a nie płynny linq. – user1729842

Odpowiedz

7
were you meaning something along the lines of: 

var newQ2 = from d in ctx.Departments 
       outer left join o in ctx.Offices on d.Id equals o.DepartmentId 
       outer left join e in ctx.Employees on o.Id equals e.OfficeId 
       group e by d into de 
       select new { 
        DepartmentId = de.Key.Id, 
        AverageAge = de.Count() == 0 ? 0 : de.Average(e => e.Age), 
       }; 

zmienione na:

var newQ2 = from d in ctx.Departments 
        join o in ctx.Offices on d.Id equals o.DepartmentId 
        join e in ctx.Employees on o.Id equals e.OfficeId 
        group e by d into de.DefaultIfEmpty() 
        select new { 
         DepartmentId = de.Key.Id, 
         DepartdentName = select d.Name from d where d.id = de.Key.Id, 
         AverageAge = de.Count() == 0 ? 0 : de.Average(e => e.Age), 
        }; 

Uzupełnienie: użyłbym sub-select, aby dopasować się dodatkowe nazwy, nie wiedząc, układ db Mam improwizowaną z kodu, ale może sprawić, że będzie on bardziej wydajny i będzie miał połączenie wieloczęściowe również w oparciu o sub-selekcje. Niestety nie mogę przetestować tego kodu w pracy, mogę go dość dobrze oszacować, ale potrzebuję więcej informacji o tym, gdzie znajdują się twoje nazwy oddziałów, jeśli potrzebujesz bardziej szczegółowej odpowiedzi :) Zmieniłem zewnętrzne lewe sprzężenia z powrotem do złączeń, przepraszam Zapomniałem w języku C# z linq, że możesz użyć DefaultIfEmpty(), aby wywołać zewnętrzne zachowanie łączenia w kodzie.

Zewnętrzne lewe sprzężenie zwróci wartości zerowe, gdy nie ma odpowiednich wartości, ale pozwoli na zwroty dla wszystkich części, które mają odpowiednią wartość. Join jednak nie zwróci żadnych pustych wpisów, które, jak podejrzewam, są przyczyną dwóch zapytań?

Jedynym zastrzeżeniem w przedstawionym przeze mnie zapytaniu jest to, że będziesz musiał wypełnić wszystkie wymagane wartości przed ich użyciem, jeśli są one zerowe, na przykład Departament będzie potrzebował pewnej logiki, aby zapełnić ją w przypadku, gdy DE ma wartość null.

+0

Myślę, że możesz zmodyfikować ten nieco dalej, aby przyciągnąć część nazwy, a następnie uważam, że drugie zapytanie OP jest niepotrzebne. – pstrjds

+2

Zewnętrzne lewe sprzężenie to dobre rozwiązanie. Ale nie jest poprawna składnia linq, jeśli składnia jest poprawna, 3 słowa będą niebieskie jako słowo kluczowe C#. – user1729842

0

Dziękuję wszystkim, mam odpowiedź:

 var q1 = 
       from d in ctx.Departments 
       from o in ctx.Offices.Where(o => o.DepartmentId == d.Id).DefaultIfEmpty() 
       from e in ctx.Employees.Where(e => e.OfficeId == o.Id).DefaultIfEmpty() 
       group e by d into de 
       select new { 
        DepartmentName = de.Key.Name, 
        AverageAge = de.Average(e => e == null ? 0 : e.Age), 
       }; 
Powiązane problemy