Linq-to-Entities: левое внешнее соединение с предложением WHERE и проекцией - PullRequest
8 голосов
/ 18 октября 2010

У меня очень много времени на то, чтобы понять, как перевести простое SQL LEFT OUTER JOIN с двумя условиями where в рабочий запрос Linq-to-Entities.Есть только две таблицы.Мне нужны значения для всех строк из Table1, независимо от совпадений в Table2, но в предложении WHERE используются поля из Table2.В SQL двумя параметрами будут Table2WhereColumn1 и Table2WhereColumn2, и запрос (который работает) выглядит следующим образом:

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

Я пытался использовать Group Join с DefaultIfEmpty(), а также неявныйобъединение (без фактического ключевого слова Join), и я получаю строки только для элементов, которые имеют значения в таблице 2.Я уверен, что это не поможет, но вот пример Linq, который я пробовал, который не работает:

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

Ответы [ 3 ]

11 голосов
/ 18 октября 2010

Просмотрите эти запросы и скажите, работают ли они на вас. Я не настроил данные для тестирования, но они должны быть в порядке.

Прошу прощения за мой микс C # и VB.NET. Раньше я был разработчиком VB.NET, но в последние пару лет я в основном работал на C #, поэтому теперь я чувствую себя там более комфортно.

Вот классы, которые я создал для 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; }
}

Теперь запрос в C # должен быть:

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,
    };

И перевод на 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) _
        }

Дайте мне знать, если они работают. Скрещенные пальцы. : -)

4 голосов
/ 07 июня 2013

Лично если есть условия для правой части левого соединения, я обычно предпочитаю включать их в критерии объединения

В этом случае SQL будет выглядеть так:1005 * Код LINQ для этого (в C #) будет выглядеть так:

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,
    };

не проверял - но должен работать

0 голосов
/ 23 октября 2017

Я не возьму кредит за этот ответ, но он великолепен: LINQ to SQL - левое внешнее соединение с несколькими условиями соединения

По сути, используйте метод расширения where в подзапросе, но вы должны использовать его до 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
...