Как сделать объединение в LINQ для нескольких полей в одном объединении

244

Мне нужно сделать запрос LINQ2DataSet, который выполняет объединение более чем в одном поле (как

var result = from x in entity
join y in entity2 
       on x.field1 = y.field1 
and 
          x.field2 = y.field2

Я еще не нашел подходящего решения (я могу добавить дополнительные ограничения к предложению where, но это далеко от подходящего решения, или использовать это решение, но это предполагает эквиджоин).

Возможно ли в LINQ объединить несколько полей в одно соединение?

РЕДАКТИРОВАТЬ

var result = from x in entity
             join y in entity2
             on new { x.field1, x.field2 } equals new { y.field1, y.field2 }

это решение, на которое я ссылаюсь, как предполагающее эквиджоин выше.

Дальнейшее редактирование

Чтобы ответить на критику, что мой первоначальный пример был эквиджоин, я признаю, что мое текущее требование - эквиойн, и я уже использовал решение, на которое я ссылался выше.

Я, однако, пытаюсь понять, какие возможности и лучшие практики я использую / должен использовать с LINQ. Мне нужно будет сделать соединение запроса диапазона дат с идентификатором таблицы, и я просто решил эту проблему. Похоже, мне нужно будет добавить диапазон дат в предложении where.

Спасибо, как всегда, за все предложения и комментарии

JohnC
источник
48
Просто для справки всем, кто читает это, если вы выполняете объединение нескольких полей в классах annon, вы ДОЛЖНЫ называть поля в обоих классах annon одинаковыми, иначе вы получите ошибки компиляции.
Отметить
6
Или, скорее, вы должны убедиться, что они имеют совпадающие имена. то есть вы можете просто назвать поля одного из типов anon, чтобы они соответствовали другому.
Том Фергюсон
1
Обратите внимание на этот ответ stackoverflow.com/a/34176502/1704458
TS
Я использовал кортежи для обеих сторон равенства, а не для объектов, и это, казалось, тоже работало.
GHZ

Ответы:

89

Решение с анонимным типом должно работать нормально. LINQ может представлять только равноценные выражения (в любом случае, с предложениями соединения), и действительно, это то, что вы сказали, что хотите выразить в любом случае на основе вашего исходного запроса.

Если вам не нравится версия с анонимным типом по какой-то конкретной причине, вы должны объяснить эту причину.

Если вы хотите сделать что-то кроме того, о чем вы изначально просили, приведите пример того, что вы действительно хотите сделать.

РЕДАКТИРОВАТЬ: Отвечая на редактирование в вопросе: да, чтобы сделать объединение «диапазон дат», вам нужно вместо этого использовать предложение where. Они действительно семантически эквивалентны, так что это просто вопрос доступных оптимизаций. Equijoins обеспечивают простую оптимизацию (в LINQ to Objects, которая включает в себя LINQ to DataSets) путем создания поиска на основе внутренней последовательности - представьте его как хеш-таблицу из ключа в последовательность записей, соответствующих этому ключу.

Делать это с диапазонами дат несколько сложнее. Однако, в зависимости от того, что именно вы подразумеваете под «объединением диапазона дат», вы можете сделать что-то похожее - если вы планируете создавать «полосы» дат (например, по одной в год), чтобы две записи, которые встречаются в тот же год (но не в тот же день) должен совпадать, тогда вы можете сделать это, просто используя эту полосу в качестве ключа. Если это сложнее, например, одна сторона объединения предоставляет диапазон, а другая сторона объединения предоставляет одну дату, соответствующую, если она попадает в этот диапазон, это будет лучше обрабатываться с помощью whereпредложения (через секундуfromоговорка) ИМО. Вы могли бы сделать что-то особенное в стиле фанк, заказав одну или другую сторону для более эффективного поиска совпадений, но это было бы большой работой - я бы сделал такую ​​вещь только после проверки, является ли производительность проблемой.

Джон Скит
источник
Спасибо, да, производительность - это мое главное беспокойство при использовании предложения where. Я предполагаю, что предложение where после объединения выполнит фильтр для большего набора данных, который можно было бы уменьшить, введя второй параметр соединения. Мне действительно нравится идея заказа, чтобы проверить, могу ли я получить повышение эффективности
johnc
Сколько у вас будет записей? Не забывайте, что упорядочение результатов для начала займет определенное время, прежде чем ...
Джон Скит
«Они на самом деле семантически эквивалентны» - нужно ли там слово «действительно»? Возможно, вы имели в виду: «Они действительно семантически эквивалентны» :)
сегодня, когда
136
var result = from x in entity
   join y in entity2 on new { x.field1, x.field2 } equals new { y.field1, y.field2 }
Кристофера
источник
Это то, что я искал, потому что у 101 Linq Samples этого не было или, по крайней мере, я видел.
Крис Марисик
1
@PeterX действительно может, посмотрите мой ответ здесь: stackoverflow.com/a/22176658/595157
niieani
13
Приведенный выше код не работает. После добавления on new { X1= x.field1, X2= x.field2 } equals new { X1=y.field1, X2= y.field2 } это сработало
Рави Рам
@ Рави Рам .. Спасибо .. Ваш комментарий помог
NMathur
80
var result = from x in entity1
             join y in entity2
             on new { X1= x.field1, X2= x.field2 } equals new { X1=y.field1, X2= y.field2 }

Это необходимо сделать, если имена столбцов различаются в двух объектах.

RealNapster
источник
6
Спасибо за упоминание разных имен столбцов. Это исправило мое плохое выражение.
Gaʀʀʏ
1
Это сработало и для меня. Если имена столбцов не совпадают, вы получите эту ошибку: «Тип одного из выражений в предложении соединения неправильный. Вывод типа не выполнен при вызове« GroupJoin ».»
Хамбадс
Спасибо за наложение ключевых переменных.
Thomas.Benz
Я получил ошибку, о которой упоминал @humbads, когда я не назвал все свойства int 'new {}'. Так что просто к сведению, если вы назовете один, вы должны также назвать остальные.
Итан Меламед
СПАСИБО ОГРОМНОЕ
Чарли, H
51

Просто чтобы завершить это с помощью эквивалентного синтаксиса цепочки методов:

entity.Join(entity2, x => new {x.Field1, x.Field2},
                     y => new {y.Field1, y.Field2}, (x, y) => x);

В то время как последний аргумент (x, y) => x- то, что вы выбираете (в приведенном выше случае мы выбираем x).

niieani
источник
31

Я думаю, что более читабельным и гибким вариантом является использование функции Where:

var result = from x in entity1
             from y in entity2
                 .Where(y => y.field1 == x.field1 && y.field2 == x.field2)

Это также позволяет легко перейти от внутреннего соединения к левому, добавив .DefaultIfEmpty ().

Алексей
источник
Как давний пользователь лямбды (в отличие от того, когда я задал вопрос), я должен был бы согласиться
johnc
Это будет медленнее?
AlfredBr
1
Я думаю, что он должен иметь ту же производительность, что и новый { ... } equals new { ... }синтаксис. LinqPad - отличный инструмент для наблюдения за поведением выражений (SQL-скрипт, если используется LINQ2SQL, деревья выражений и т. Д.)
Алексей
Насколько я заметил, он производит CROSS JOIN вместо INNER JOIN
Mariusz
@Mariusz Да, имеет смысл генерировать CROSS JOIN + WHERE вместо INNER JOIN. Для простых запросов я ожидаю, что анализатор сгенерирует очень похожее.
Алексей
10
var result = from x in entity
             join y in entity2
             on new { X1= x.field1, X2= x.field2 } equals new { X1=y.field1, X2= y.field2 }
             select new 
             {
               /// Columns
              };
user3966657
источник
8

Вы могли бы сделать что-то вроде (ниже)

var query = from p in context.T1

        join q in context.T2

        on

        new { p.Col1, p.Col2 }

        equals

         new { q.Col1, q.Col2 }

        select new {p...., q......};
Perpetualcoder
источник
Как я уже говорил в вопросе, который требует эквисоединения
JohnC
7

Используя оператор соединения, вы можете выполнять только эквайоины. Другие типы объединений могут быть созданы с использованием других операторов. Я не уверен, будет ли проще использовать именно эти методы для точного соединения, которое вы пытаетесь сделать, или изменив предложение where. Документацию по предложению соединения можно найти здесь . В MSDN есть статья об операциях объединения с несколькими ссылками на примеры других объединений.

tvanfosson
источник
3

Если имена полей различаются в сущностях

var result = from x in entity
   join y in entity2 on 
          new {
                field1=   x.field1,
               field2 =  x.field2 
             } 
          equals
         new { 
                field1= y.field1,
                field2=  y.myfield
              }
select new {x,y});
Махеш
источник
Спасибо. Соответствие имени было частью, которую я пропустил.
Бретт
2

Как полная цепочка методов, которая будет выглядеть так:

lista.SelectMany(a => listb.Where(xi => b.Id == a.Id && b.Total != a.Total),
                (a, b) => new ResultItem
                {
                    Id = a.Id,
                    ATotal = a.Total,
                    BTotal = b.Total
                }).ToList();
Адам Гарнер
источник
-2
from d in db.CourseDispatches
                             join du in db.DispatchUsers on d.id equals du.dispatch_id
                             join u in db.Users on du.user_id equals u.id
                             join fr in db.Forumreports on (d.course_id + '_' + du.user_id)  equals  (fr.course_id + '_'+ fr.uid)

это работает для меня

user2745564
источник
Это для множественного объединения, он хочет объединить несколько полей в одном соединении
theLaw
-3

Объявите класс (тип) для хранения элементов, к которым вы хотите присоединиться. В приведенном ниже примере объявить JoinElement

 public class **JoinElement**
{
    public int? Id { get; set; }
    public string Name { get; set; }

}

results = from course in courseQueryable.AsQueryable()
                  join agency in agencyQueryable.AsQueryable()
                   on new **JoinElement**() { Id = course.CourseAgencyId, Name = course.CourseDeveloper } 
                   equals new **JoinElement**() { Id = agency.CourseAgencyId, Name = "D" } into temp1
Вен
источник
1
На этот вопрос уже отвечали 9 лет назад ... какую ценность несет этот ответ?
Мацей Юречко