Як зробити приєднання до LINQ на декількох полях в одному об'єднанні


244

Мені потрібно зробити запит LINQ2DataSet, який робить з'єднання у більш ніж одному полі (як

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

Я ще не знайшов підходящого рішення (я можу додати додаткові обмеження до пункту де, але це далеко не підходяще рішення, або використовувати це рішення, але це передбачає еквіорт).

Чи можливо в LINQ приєднатися до декількох полів за один приєднання?

EDIT

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

- це рішення, на яке я посилався як припущення про еквіорт вище.

Подальше EDIT

Щоб відповісти на критику, що моїм оригінальним прикладом був еджойот, я визнаю це: Моя поточна вимога - це еквіджойн, і я вже застосував рішення, на яке я посилався вище.

Я, однак, намагаюся зрозуміти, які можливості та найкращі практики, які я маю / повинен використовувати з LINQ. Мені незабаром потрібно буде зробити запит на діапазон дат, з'єднавшись з ідентифікатором таблиці, і я лише попереджав цю проблему. Схоже, мені доведеться додати діапазон дат у пункті де.

Дякуємо, як завжди, за всі подані пропозиції та коментарі


48
Просто FYI для тих, хто читає це, якщо ви приєднуєтесь до декількох полів у класах annon, ви ОБОВ'ЯЗКОВО назвати поля в обох класах annon однаково, інакше ви отримаєте помилки компіляції.
Позначте

6
А точніше, ви повинні переконатися, що вони мають відповідні імена. тобто ви можете просто назвати поля одного з типів anon, щоб вони відповідали іншому.
Том Фергюсон

1
Зверніть увагу на цю відповідь stackoverflow.com/a/34176502/1704458
TS

Я використовував кортежі для будь-якої сторони рівних, а не для предметів, і, здавалося, це теж працює.
GHZ

Відповіді:


89

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

Якщо вам не подобається версія з анонімним типом з якоїсь конкретної причини, вам слід пояснити цю причину.

Якщо ви хочете зробити щось інше, ніж те, що ви спочатку просили, будь ласка, наведіть приклад того, що ви насправді хочете зробити.

РЕДАКТУВАННЯ: Відповідаючи на редагування у запитанні: так, щоб зробити приєднання "діапазону дат", потрібно замість цього використовувати пункт "де". Вони насправді семантично еквівалентні, тому це лише питання наявних оптимізацій. Equijoins забезпечують просту оптимізацію (в LINQ до об’єктів, яка включає LINQ до наборів даних), створюючи пошук на основі внутрішньої послідовності - подумайте про це як хеш-таблицю від ключа до послідовності записів, що відповідають цьому ключу.

Зробити це з діапазонами дат дещо складніше. Однак, залежно від того, що саме ви маєте на увазі під "приєднанням діапазону дат", ви можете зробити щось подібне - якщо ви плануєте створити "смуги" дат (наприклад, один на рік), таким чином, щоб дві записи, які відбуваються в той же рік (але не в ту ж дату) повинен відповідати, то ви можете це зробити, просто використовуючи цю смугу як ключ. Якщо це складніше, наприклад, одна сторона з'єднання надає діапазон, а інша сторона з'єднання надає єдину дату, яка відповідає, якщо вона потрапляє в цей діапазон, що краще буде оброблятися whereпунктом (через секундуfromпункт) ІМО. Ви можете зробити якусь особливо прикольну магію, наказавши одній чи іншій стороні знайти ефективніші матчі, але це буде багато роботи - я б робив таку річ лише після перевірки, чи не є продуктивність.


Дякую, так, ефективність - це моя головна проблема з використанням пункту "де". Я здогадуюсь, де пункт після з'єднання виконає фільтр на більшому наборі даних, який міг би бути зменшений введенням другого параметра приєднання. Мені подобається ідея замовити тестування, чи зможу я отримати підвищення ефективності
johnc

Скільки записів у вас буде? Не забувайте, що для замовлення результатів для початку знадобиться певна кількість часу, щоб почати з ...
Джон Скіт

"Вони насправді семантично еквівалентні" - чи нам потрібне слово "справді"? Можливо, ви мали на увазі «Вони справді семантично рівнозначні» :)
onedaywhen

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 цього не було, або принаймні, що я бачив.
Кріс Марісіч

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 } Це спрацювало
Раві Рам

@Ravi Ram .. Дякую .. ваш коментар допоміг
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 }

Це потрібно зробити, якщо назви стовпців відрізняються у двох сутностях.


6
Дякуємо, що згадали про різні назви стовпців. Це зафіксувало моє погане вираження.
Gaʀʀʏ

1
Це працювало і для мене. Якщо імена стовпців не збігаються, ви отримаєте цю помилку: "Тип одного з виразів у пункті приєднання є невірним. Введення тексту не вдалося у виклику" GroupJoin "."
хабади

Дякуємо за те, що ви знайдете ключові змінні.
Thomas.Benz

У мене з’явилася помилка, яку згадував @humbads, коли я не назвав усі властивості int 'new {}'. Тож просто fyi, якщо ви назвали його, ви також повинні назвати решту.
Етан Меламед

ДЯКУЙТЕ ТАКЕ МНОГО
Charly H

51

Просто для завершення цього за допомогою синтаксису ланцюга еквівалентного методу:

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

Тоді як останній аргумент (x, y) => x- це те, що ви вибираєте (у наведеному вище випадку ми вибираємо x).


31

Я думаю, що більш зрозумілим та гнучким варіантом є використання функції Where:

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

Це також дозволяє легко переходити від внутрішнього приєднання до лівого, додаючи .DefaultIfEmpty ().


Як давно користувач лямбда зараз (на відміну від того, коли я задавав питання), я повинен був би погодитися
johnc

Це буде повільніше?
АльфредБр

1
Я думаю, він повинен мати таку ж ефективність, як і новий { ... } equals new { ... }синтаксис. LinqPad - чудовий інструмент для того, щоб побачити, як поводяться вирази (сценарій SQL, якщо використовується LINQ2SQL, дерева виразів тощо)
Олексій

Наскільки я помітив, це виробляє CROSS JOIN замість ВНУТРІШНЬОГО ПРИЄДНАННЯ
Mariusz

@Mariusz Так, має сенс створити CROSS JOIN + WHERE замість INNER JOIN. Для простих запитів я очікую, що аналізатор генерує дуже схожий.
Олексій


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

Як я вже говорив в питанні, який вимагає еквісоедіненія
JohnC

7

Використовуючи оператор приєднання, ви можете виконувати лише еджійойн. Інші типи з'єднань можуть бути побудовані за допомогою інших операторів. Я не впевнений, чи точне приєднання, яке ви намагаєтесь зробити, було б простіше за допомогою цих методів або змінивши пункт де. Документацію щодо підключення можна знайти тут . У MSDN також є стаття про операції приєднання з декількома посиланнями на приклади інших приєднань.


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)

це працює для мене


Це для багаторазового приєднання, він хоче зробити приєднання з декількома полями в один приєднання
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 років тому ... яке значення приносить ця відповідь?
Maciej Jureczko
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.