СВЯЗЬ с объектом: несколько условий соединения

#linq #linq-to-entities

#linq #привязка к объектам

Вопрос:

Существует множество сообщений, касающихся LINQ и множественных соединений. Однако я не нашел никакого решения для объединения, которое я хотел бы сделать.

Эквивалент SQL будет примерно таким:

 SELECT * FROM table1 a
LEFT JOIN table2 b ON a.col1 = b.key1 AND
a.col2 = b.key2 AND
b.from_date <= now() AND
b.deleted = 0;
  

Вот один из многочисленных запросов linq, которые я пытался выполнить

 var query = (from x in context.table1
             join y in context.table2 on new {x.col1, x.col2} equals {b.key1, b.key2} 
             into result
             from result......
  

Как я могу добавить дополнительные условия даты и удаленного флага?
Если я использую .Где условия, то это рассматривается как внутреннее соединение, а не левое соединение.

Ответ №1:

Другой способ может быть следующим

 var query = (from x in context.table1 
             join y in context.table2 on 
             new  {
                  Key1 = x.col1, 
                  Key2 = x.col2,
                  Key3 = true,
                  Key4 = true
                 }
             equals
             new {
                  Key1 = y.key1, 
                  Key2 =  y.key2,
                  Key3 = y.from_date< DateTime.Now,
                  Key4 = !y.deleted
                 }  
             into result
from r in result.DefaultIfEmpty()
select new  {x.Something, r.Something}
  

Комментарии:

1. Я также хотел бы добавить комментарий для всех, кто может наткнуться на этот ответ. Имена свойств должны совпадать для двух объектов, иначе компилятор будет жаловаться на помехи типа. Но это можно преодолеть, установив имя свойства явно, как показано в приведенном выше примере.

Ответ №2:

LINQ поддерживает как синтаксис соединения, так и более старый синтаксис ANSI-82 WHERE . Используя более позднее, вы могли бы делать то, что искали во внутреннем соединении с

 var nowTime = DateTime.Now;
var query = from a in context.table1
            from b in context.table2
            where a.col1 == b.key1
                 amp;amp; a.col2 == b.key2 
                 amp;amp; b.from_date < nowTime
                 amp;amp; b.deleted == false
            select ???;
  

Для внешнего соединения я предпочитаю синтаксис, использующий гибрид where и select many . (Поймите, что порядок в запросе LINQ не должен имитировать то, что вы сделали бы в SQL, и порядок более гибкий.)

 var nowTime = DateTime.Now;
var query = from b in context.table2
            from a1 in a.Where(a2 => 
                b.key1 = a.col amp;amp; 
                b.key2 = a.col2 amp;amp;
                b.from_date < nowTime amp;amp;
                b.deleted == false).DefaultIfEmpty()
            select ???;
  

Комментарии:

1. Синтаксис ANSI-82 WHERE создает перекрестное соединение

2. @shrutyzet это с EF 6 или EF Core?

3. EF 6, возможно, вы забыли добавить. DefaultIfEmpty()

4. Я пропустил левый внешний элемент из OP. Вы можете использовать синтаксис ANSI-82 для left outer, а также, как я описал в этом сообщении в блоге .

5. 1-й linq создает перекрестное соединение. Не внутреннее соединение. Протестировано в EF Core 3.1

Ответ №3:

У меня возникла проблема с присвоением имен свойствам в анонимном объекте:

 var subscriptions = context.EmailSubscription.Join(context.EmailQueue,
                    es => new { es.Id, 9 },
                    eq => new { eq.EmailSubscriptionId, eq.EmailTemplateId },
                    (es, eq) => new { es.Id, eq.Id }
                ).ToList();
  

Компилятор не был доволен, поэтому приведенный выше ответ помогает мне понять, что было не так, и вот мое рабочее решение. Мне потребовалось некоторое время, чтобы найти глупую ошибку 🙂 :

 var subscriptions = context.EmailSubscription.Join(context.EmailQueue,
                    es => new { EmailSubscriptionId = es.Id, EmailTemplateId  = 9 },
                    eq => new { eq.EmailSubscriptionId, eq.EmailTemplateId },
                    (es, eq) => new { es.Id, eq.Id }
                ).ToList();
  

Комментарии:

1. хотел бы я получить дополнительные объяснения по этому поводу

2. Это предпочтительный синтаксис efcore для объединения . В приведенном выше примере es и eq являются псевдонимами. Они передаются по конвейеру в новый анонимный объект с произвольными свойствами, такими как emailSubscriptionId, присвоенными значению, на которое ссылается es. Например, идентификатор.

Ответ №4:

Не могли бы вы просто отфильтровать 1-й результирующий набор вторым запросом?

 var query = (from x in context.table1 
             join y in context.table2 on new {x.col1, x.col2} equals {b.key1, b.key2}  
             into result
query = from x in query
        where ...
  

Будет ли это работать?

Ответ №5:

В дополнение к ответу @Muhammad Adeel Zahid вы могли бы использовать также несколько условий, таких как:

 new
{
Key1 = ppl.PeopleId,
Key2 = true,
Key3 = true
}
equals
new
{
Key1 = y.PeopleId,
Key2 = !y.IsDeleted,
Key3 = (y.RelationshipType == 2 || y.RelationshipType == 4)
}