2010-10-18 13 views
8

Mam czas heckuva zastanawiający się, jak przetłumaczyć proste SQL LEFT OUTER JOIN z dwoma warunkami, gdzie klauzula w działającym kwerendzie Linq-to-Entities . Są tylko dwie tabele. Potrzebuję wartości dla wszystkich wierszy z tabeli 1, niezależnie od dopasowań w tabeli 2, ale klauzula WHERE używa pól z tabeli 2. W SQL, oba parametry byłyby Table2WhereColumn1 i Table2WhereColumn2, a zapytanie (które działa) wygląda następująco:Linq-to-Entities: LEFT OUTER JOIN z klauzul WHERE i projekcją

SELECT t1.Table1Id, 
    t1.FieldDescription, 
    t2.FieldValue 
FROM Table1 t1 WITH (NOLOCK) 
LEFT JOIN Table2 t2 WITH (NOLOCK) ON t1.Table1Id = t2.Table1Id 
WHERE (t2.Table2WhereColumn1 = @someId OR t2.Table2WhereColumn1 IS NULL) 
AND (t2.Table2WhereColumn2 = @someOtherId OR t2.Table2WhereColumn2 IS NULL) 
ORDER BY t1.OrderByColumn 

Próbowałem za pomocą Group Join z DefaultIfEmpty(), jak również ukryte join (bez rzeczywista Join słowo kluczowe), a otrzymuję tylko wiersze dla elementów, które mają wartości w tabeli 2. Jestem pewien, że to nie pomoże, ale tutaj jest przykład LINQ Próbowałem, że nie działa:

Public Shared Function GetProfilePreferencesForCedent(ByVal dc As EntityContext, _ 
                 ByVal where1 As Int32, _ 
                 ByVal where2 As Int32) _ 
                As IQueryable(Of ProjectedEntity) 
    Return From t1 In dc.Table1 
      Group Join t2 In dc.Table2 _ 
       On t1.Table1Id Equals t2.Table1Id _ 
       Into t2g1 = Group _ 
      From t2gx In t2g1.DefaultIfEmpty(Nothing) 
      Where (t2gx.Table2Where1 = where1 Or t2gx.Table2Where1 = Nothing) _ 
       And (t2gx.Table2Where2 = where2 Or t2gx.Table2Where2 = Nothing) 
      Order By t1.SortOrder 
      Select New ProjectedEntity With { 
       .Table1Id = t1.Table1Id, _ 
       .FieldDescription = t1.FieldDescription, _ 
       .FieldValue = If(t2gx Is Nothing, String.Empty, t2gx.FieldValue) _ 
      } 
End Function 

Odpowiedz

10

mieć go na te pytania i powiedz mi, czy pracują dla ciebie. Nie skonfigurowałem danych do przetestowania, ale powinny być w porządku.

Proszę wybaczyć mój miks C# & VB.NET. Kiedyś byłem programistą VB.NET, ale w ciągu ostatnich kilku lat pracowałem głównie w C#, więc teraz czuję się tam bardziej komfortowo.

Oto klas I utworzone dla Table1 & Table2:

public class Table1 
{ 
    public int Table1Id { get; set; } 
    public string FieldDescription { get; set; } 
    public int OrderByColumn { get; set; } 
} 
public class Table2 
{ 
    public int Table1Id { get; set; } 
    public string FieldValue { get; set; } 
    public int Table2WhereColumn1 { get; set; } 
    public int Table2WhereColumn2 { get; set; } 
} 

Teraz kwerendy w języku C# powinno być:

var query = 
    from t1 in Table1 
    join t2 in Table2 on t1.Table1Id equals t2.Table1Id into _Table2 
    from _t2 in _Table2.DefaultIfEmpty() 
    where _t2 == null ? true : 
     _t2.Table2WhereColumn1 == @someId 
     && _t2.Table2WhereColumn2 == @someOtherId 
    orderby t1.OrderByColumn 
    select new 
    { 
     t1.Table1Id, 
     t1.FieldDescription, 
     FieldValue = _t2 == null ? "" : _t2.FieldValue, 
    }; 

a tłumaczenie w VB.NET:

Dim query = _ 
    From t1 In Table1 _ 
    Group Join t2 In Table2 On t1.Table1Id Equals t2.Table1Id Into _Table2 = Group _ 
    From _t2 In _Table2.DefaultIfEmpty() _ 
    Where If(_t2 Is Nothing, True, _t2.Table2WhereColumn1 = someId AndAlso _ 
            _t2.Table2WhereColumn2 = someOtherId) _ 
    Order By t1.OrderByColumn _ 
    Select New With { _ 
      .Table1Id = t1.Table1Id, _ 
      .FieldDescription = t1.FieldDescription, _ 
      .FieldValue = If(_t2 Is Nothing, "", _t2.FieldValue) _ 
     } 

Daj mi znać, jeśli działają. Skrzyżowane palce. :-)

+0

Outstanding! Działa jak marzenie. Nie jestem pewien, czy rozumiem, jak działa klauzula Where, ale działa. –

3

Osobiście jeśli istnieją warunki do prawej strony z lewej przyłączyć ja generalnie wolę je do kryteriów łączą

W tym przypadku SQL wyglądałby następująco:

SELECT t1.Table1Id, 
     t1.FieldDescription, 
     t2.FieldValue 
FROM Table1 t1 WITH (NOLOCK) 
LEFT JOIN Table2 t2 WITH (NOLOCK) ON t1.Table1Id = t2.Table1Id 
            AND t2.Table2WhereColumn1 = @someId 
            AND t2.Table2WhereColumn2 = @someOtherId 
ORDER BY t1.OrderByColumn 

Kod LINQ do tego (w języku C#) wyglądałby następująco:

var query = 
    from t1 in Table1 
    join t2 in Table2 on new{a = t1.Table1Id, b = someId, c = someotherId} 
         equals new {a = t2.Table1Id b = t2.Table2WhereColumn1, c = Table2WhereColumn2} 
    into _Table2 
    from _t2 in _Table2.DefaultIfEmpty() 
    orderby t1.OrderByColumn 
    select new 
    { 
     t1.Table1Id, 
     t1.FieldDescription, 
     FieldValue = _t2 == null ? "" : _t2.FieldValue, 
    }; 

nie testowano go - ale powinien działać

+0

+1 To jest sposób, w jaki lubię generować właściwy stół Linq, gdzie warunki po lewej stronie łączą się, ponieważ wydaje mi się, że jest on bardziej podobny do składni języka SQL, do której jestem przyzwyczajony. –

+0

Naprawdę dobra składnia. Muszę zgodzić się na bardziej przypominający język SQL. – Tobias

0

nie wezmę kredyt dla tej odpowiedzi, ale jest wspaniały: LINQ to SQL - Left Outer Join with multiple join conditions

Zasadniczo przedłużenie zastosowanie metoda gdzie klauzula na podzapytaniu ale trzeba go używać przed DefaultIfEmpty():

from p in context.Periods 
join f in context.Facts on p.id equals f.periodid into fg 
from fgi in fg.Where(f => f.otherid == 17).DefaultIfEmpty() 
where p.companyid == 100 
select f.value