ЛЕВЫЙ ВНЕШНИЙ РЕЙС

539

Как выполнить левое внешнее соединение в C # LINQ для объектов без использования join-on-equals-intoпредложений? Есть ли способ сделать это сwhere пункта? Правильная проблема: для внутреннего объединения легко, и у меня есть решение как это

List<JoinPair> innerFinal = (from l in lefts from r in rights where l.Key == r.Key
                             select new JoinPair { LeftId = l.Id, RightId = r.Id})

но для левого внешнего соединения мне нужно решение. Мой что-то вроде этого, но он не работает

List< JoinPair> leftFinal = (from l in lefts from r in rights
                             select new JoinPair { 
                                            LeftId = l.Id, 
                                            RightId = ((l.Key==r.Key) ? r.Id : 0
                                        })

где JoinPair является классом:

public class JoinPair { long leftId; long rightId; }
игрушечный
источник
2
Можете ли вы привести пример того, чего вы пытаетесь достичь?
Джерон
обычное левое внешнее соединение выглядит примерно так: var a = от b в bb объединяет c в cc на b.bbbbb равно c.ccccc в dd из d в dd.DefaultIfEmpty () select b.sss; Мой вопрос: есть ли способ сделать это без использования выражений join-on-equals-into, например: var a = from b в bb из c в cc, где b.bbb == c.cccc ... и так далее ... .
игрушка
1
конечно, есть, но вы должны опубликовать пример своего кода, который у вас уже есть, чтобы люди могли дать вам лучший ответ
лень
Я искал JOIN "Left исключая " (и я перепутал его с понятием "OUTER"). Этот ответ был ближе к тому, что я хотел.
Красный горох

Ответы:

598

Как указано на:

101 LINQ Samples - Левое внешнее соединение

var q =
    from c in categories
    join p in products on c.Category equals p.Category into ps
    from p in ps.DefaultIfEmpty()
    select new { Category = c, ProductName = p == null ? "(No products)" : p.ProductName };
ajay_whiz
источник
7
Я пытаюсь сделать то же самое, но получаю сообщение об ошибке в операторе соединения, который говорит: «Тип одного из выражений в предложении соединения неверен».
Бадхон Джайн
3
@jain, если ваши типы отличаются, объединение не будет работать. Вероятно, ваши ключи имеют разные типы данных. Например, оба ключа int?
Yooakim
2
Какое решение джайн? Я также сталкиваюсь с той же ошибкой, и типы одинаковы и в моем случае.
Сандип
1
@ Сандип проверьте свои ключи, где вы присоединились к нему. Предположим, что если они имеют тип string и int, просто преобразуйте строковый ключ в int.
Анкит
2
обновленная ссылка: 101 LINQ Samples - левое внешнее соединение
BukeMan
546

Если используется поставщик LINQ, управляемый базой данных, значительно более читаемое левое внешнее объединение можно записать так:

from maintable in Repo.T_Whatever 
from xxx in Repo.T_ANY_TABLE.Where(join condition).DefaultIfEmpty()

Если вы пропустите, у DefaultIfEmpty()вас будет внутреннее соединение.

Возьми принятый ответ:

  from c in categories
    join p in products on c equals p.Category into ps
    from p in ps.DefaultIfEmpty()

Этот синтаксис очень сбивает с толку, и неясно, как он работает, когда вы хотите оставить объединенные таблицы MULTIPLE.

Примечание
Следует отметить, чтоfrom alias in Repo.whatever.Where(condition).DefaultIfEmpty() это то же самое, что и external-apply / left-join-lateral, которую любой (приличный) оптимизатор базы данных вполне может преобразовать в левое объединение, если вы не вводите для каждой строки -значения (то есть фактическое внешнее применение). Не делайте этого в Linq-2-Objects (потому что нет никакого DB-оптимизатора, когда вы используете Linq-to-Objects).

Подробный пример

var query2 = (
    from users in Repo.T_User
    from mappings in Repo.T_User_Group
         .Where(mapping => mapping.USRGRP_USR == users.USR_ID)
         .DefaultIfEmpty() // <== makes join left join
    from groups in Repo.T_Group
         .Where(gruppe => gruppe.GRP_ID == mappings.USRGRP_GRP)
         .DefaultIfEmpty() // <== makes join left join

    // where users.USR_Name.Contains(keyword)
    // || mappings.USRGRP_USR.Equals(666)  
    // || mappings.USRGRP_USR == 666 
    // || groups.Name.Contains(keyword)

    select new
    {
         UserId = users.USR_ID
        ,UserName = users.USR_User
        ,UserGroupId = groups.ID
        ,GroupName = groups.Name
    }

);


var xy = (query2).ToList();

При использовании с LINQ 2 SQL он будет хорошо переведен в следующий очень разборчивый SQL-запрос:

SELECT 
     users.USR_ID AS UserId 
    ,users.USR_User AS UserName 
    ,groups.ID AS UserGroupId 
    ,groups.Name AS GroupName 
FROM T_User AS users

LEFT JOIN T_User_Group AS mappings
   ON mappings.USRGRP_USR = users.USR_ID

LEFT JOIN T_Group AS groups
    ON groups.GRP_ID == mappings.USRGRP_GRP

Редактировать:

См. Также « Преобразование запроса SQL Server в запрос Linq » для более сложного примера.

Кроме того, если вы делаете это в Linq-2-Objects (вместо Linq-2-SQL), вы должны сделать это старомодным способом (потому что LINQ to SQL переводит это правильно, чтобы объединить операции, но поверх объектов этот метод вызывает полное сканирование и не использует поиск по индексу, вообще ...):

    var query2 = (
    from users in Repo.T_Benutzer
    join mappings in Repo.T_Benutzer_Benutzergruppen on mappings.BEBG_BE equals users.BE_ID into tmpMapp
    join groups in Repo.T_Benutzergruppen on groups.ID equals mappings.BEBG_BG into tmpGroups
    from mappings in tmpMapp.DefaultIfEmpty()
    from groups in tmpGroups.DefaultIfEmpty()
    select new
    {
         UserId = users.BE_ID
        ,UserName = users.BE_User
        ,UserGroupId = mappings.BEBG_BG
        ,GroupName = groups.Name
    }

);
Стефан Штайгер
источник
21
Этот ответ действительно полезен. Спасибо, что предложили синтаксис, который понятен.
Крис Марисик
3
WTB совместимый с NHibernate запрос LINQ ... :)
mxmissile
30
LINQ to SQL переводит это правильно, чтобы объединить операции. Над объектами, однако, этот метод вызывает полное сканирование, поэтому в официальной документации предлагается решение для группового объединения, которое может использовать преимущества хэшей для индексации поисков.
Тамир Даниэли
3
Я думаю, что синтаксис явного joinнамного более читабелен и понятен, чем whereсопровождаемыйDefaultIfEmpty
FindOut_Quran
1
@ user3441905: Пока вам нужно просто соединить таблицу a с таблицей b, это может быть. Но как только у вас будет больше, этого не будет. Но даже для двух столов, я думаю, это слишком многословно. Популярное мнение также кажется против вас, так как этот ответ начался с 0, когда в верхнем ответе уже было более 90 голосов.
Стефан Штайгер,
132

Использование лямбда-выражения

db.Categories    
  .GroupJoin(db.Products,
      Category => Category.CategoryId,
      Product => Product.CategoryId,
      (x, y) => new { Category = x, Products = y })
  .SelectMany(
      xy => xy.Products.DefaultIfEmpty(),
      (x, y) => new { Category = x.Category, Product = y })
  .Select(s => new
  {
      CategoryName = s.Category.Name,     
      ProductName = s.Product.Name   
  });
N качалка
источник
8
И Join, и GroupJoin не поддерживают левое соединение. Хитрость с использованием GroupJoin состоит в том, что вы можете иметь пустые группы и затем преобразовывать эти пустые группы в пустые значения. DefaultIfEmpty просто делает это, то есть Enumerable.Empty<Product>.DefaultIfEmpty()будет возвращать IEnumerable с одним значением default(Product).
Тамир Даниэли
61
Все это для выполнения левого соединения ??
FindOut_Quran
7
Спасибо за это! Не слишком много примеров лямбда-выражений, у меня это сработало.
Йохан Хенкенс
1
Спасибо за ответ. Это дало самую близкую вещь к необработанному SQL, который я написал за последние годы
Джон Гатого,
1
На самом деле не нужен последний Select (), anon obj в SelectMany () может быть реорганизован для того же вывода. Другая мысль состоит в том, чтобы проверить y на ноль, чтобы имитировать более близкую эквивалентность влево.
Денни Джейкоб
46

Теперь в качестве метода расширения:

public static class LinqExt
{
    public static IEnumerable<TResult> LeftOuterJoin<TLeft, TRight, TKey, TResult>(this IEnumerable<TLeft> left, IEnumerable<TRight> right, Func<TLeft, TKey> leftKey, Func<TRight, TKey> rightKey,
        Func<TLeft, TRight, TResult> result)
    {
        return left.GroupJoin(right, leftKey, rightKey, (l, r) => new { l, r })
             .SelectMany(
                 o => o.r.DefaultIfEmpty(),
                 (l, r) => new { lft= l.l, rght = r })
             .Select(o => result.Invoke(o.lft, o.rght));
    }
}

Используйте, как вы обычно используете join:

var contents = list.LeftOuterJoin(list2, 
             l => l.country, 
             r => r.name,
            (l, r) => new { count = l.Count(), l.country, l.reason, r.people })

Надеюсь, это сэкономит вам время.

Матас Вайткявичюс
источник
44

Посмотрите на этот пример . Этот запрос должен работать:

var leftFinal = from left in lefts
                join right in rights on left equals right.Left into leftRights
                from leftRight in leftRights.DefaultIfEmpty()
                select new { LeftId = left.Id, RightId = left.Key==leftRight.Key ? leftRight.Id : 0 };
Devart
источник
3
Может rбыть доступны в выбранном пункте после использования объединения в?
Фархад Ализаде Нури
@FarhadAlizadehNoori Да, это возможно.
Po-ta-toe
Автор, вероятно, хотел повторно использовать rво втором fromпункте. т.е. в from r in lrs.DefaultIfEmpty()противном случае этот запрос не имеет особого смысла и, вероятно, даже не компилируется из- rза неконтролируемого выбора.
Саиб Амини
@Devart, когда я прочитал твой запрос, он напомнил мне фильм Clockwiseс Джоном Клизом, смеется.
Матас Вайткявичюс
1
Слева направо, слева направо, справа налево в leftRights ... О боже ... Синтаксис использования LEFT OUTER JOIN в LINQ действительно неясен, но эти имена действительно делают его еще более неясным.
Майк Гледхилл
19

Реализация левого внешнего соединения методами расширения может выглядеть так

public static IEnumerable<Result> LeftJoin<TOuter, TInner, TKey, Result>(
  this IEnumerable<TOuter> outer, IEnumerable<TInner> inner
  , Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector
  , Func<TOuter, TInner, Result> resultSelector, IEqualityComparer<TKey> comparer)
  {
    if (outer == null)
      throw new ArgumentException("outer");

    if (inner == null)
      throw new ArgumentException("inner");

    if (outerKeySelector == null)
      throw new ArgumentException("outerKeySelector");

    if (innerKeySelector == null)
      throw new ArgumentException("innerKeySelector");

    if (resultSelector == null)
      throw new ArgumentException("resultSelector");

    return LeftJoinImpl(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer ?? EqualityComparer<TKey>.Default);
  }

  static IEnumerable<Result> LeftJoinImpl<TOuter, TInner, TKey, Result>(
      IEnumerable<TOuter> outer, IEnumerable<TInner> inner
      , Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector
      , Func<TOuter, TInner, Result> resultSelector, IEqualityComparer<TKey> comparer)
  {
    var innerLookup = inner.ToLookup(innerKeySelector, comparer);

    foreach (var outerElment in outer)
    {
      var outerKey = outerKeySelector(outerElment);
      var innerElements = innerLookup[outerKey];

      if (innerElements.Any())
        foreach (var innerElement in innerElements)
          yield return resultSelector(outerElment, innerElement);
      else
        yield return resultSelector(outerElment, default(TInner));
     }
   }

Затем селектор результатов должен позаботиться о пустых элементах. Fx.

   static void Main(string[] args)
   {
     var inner = new[] { Tuple.Create(1, "1"), Tuple.Create(2, "2"), Tuple.Create(3, "3") };
     var outer = new[] { Tuple.Create(1, "11"), Tuple.Create(2, "22") };

     var res = outer.LeftJoin(inner, item => item.Item1, item => item.Item1, (it1, it2) =>
     new { Key = it1.Item1, V1 = it1.Item2, V2 = it2 != null ? it2.Item2 : default(string) });

     foreach (var item in res)
       Console.WriteLine(string.Format("{0}, {1}, {2}", item.Key, item.V1, item.V2));
   }
Bertrand
источник
4
Однако это только опция для LINQ для объектов, и она не сможет транслировать запрос каким-либо поставщикам запросов, что является наиболее распространенным вариантом использования этой операции.
Serv
13
Но вопрос был «Как выполнить левое внешнее объединение в C # LINQ для объектов ...»
Бертран
12

взгляните на этот пример

class Person
{
    public int ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Phone { get; set; }
}

class Pet
{
    public string Name { get; set; }
    public Person Owner { get; set; }
}

public static void LeftOuterJoinExample()
{
    Person magnus = new Person {ID = 1, FirstName = "Magnus", LastName = "Hedlund"};
    Person terry = new Person {ID = 2, FirstName = "Terry", LastName = "Adams"};
    Person charlotte = new Person {ID = 3, FirstName = "Charlotte", LastName = "Weiss"};
    Person arlene = new Person {ID = 4, FirstName = "Arlene", LastName = "Huff"};

    Pet barley = new Pet {Name = "Barley", Owner = terry};
    Pet boots = new Pet {Name = "Boots", Owner = terry};
    Pet whiskers = new Pet {Name = "Whiskers", Owner = charlotte};
    Pet bluemoon = new Pet {Name = "Blue Moon", Owner = terry};
    Pet daisy = new Pet {Name = "Daisy", Owner = magnus};

    // Create two lists.
    List<Person> people = new List<Person> {magnus, terry, charlotte, arlene};
    List<Pet> pets = new List<Pet> {barley, boots, whiskers, bluemoon, daisy};

    var query = from person in people
        where person.ID == 4
        join pet in pets on person equals pet.Owner  into personpets
        from petOrNull in personpets.DefaultIfEmpty()
        select new { Person=person, Pet = petOrNull}; 



    foreach (var v in query )
    {
        Console.WriteLine("{0,-15}{1}", v.Person.FirstName + ":", (v.Pet == null ? "Does not Exist" : v.Pet.Name));
    }
}

// This code produces the following output:
//
// Magnus:        Daisy
// Terry:         Barley
// Terry:         Boots
// Terry:         Blue Moon
// Charlotte:     Whiskers
// Arlene:

Теперь вы можете, include elements from the leftдаже если этот элемент has no matches in the right, в нашем случае мы восстановили, Arleneдаже если он не имеет соответствия в правом

вот ссылка

Как выполнить левое внешнее соединение (Руководство по программированию в C #)

Башир Аль-Момани
источник
вывод должен быть: Арлин: не существует
user1169587
10

Это общая форма (как уже указано в других ответах)

var c =
    from a in alpha
    join b in beta on b.field1 equals a.field1 into b_temp
    from b_value in b_temp.DefaultIfEmpty()
    select new { Alpha = a, Beta = b_value };

Однако вот объяснение, которое, я надеюсь, прояснит, что это на самом деле означает!

join b in beta on b.field1 equals a.field1 into b_temp

по существу создает отдельный результирующий набор b_temp, который фактически содержит нулевые «строки» для записей справа (записи в «b»).

Тогда следующая строка:

from b_value in b_temp.DefaultIfEmpty()

..iterate над этим результирующим набором, устанавливая нулевое значение по умолчанию для 'строки' с правой стороны, и устанавливая результат соединения строки правой стороны со значением 'b_value' (то есть значением, которое находится справа стороны, если есть совпадающая запись, или 'null', если ее нет).

Теперь, если правая часть является результатом отдельного запроса LINQ, он будет состоять из анонимных типов, которые могут быть либо «чем-то», либо «нулем». Однако, если он перечислим (например, List - где MyObjectB - это класс с 2 полями), тогда можно быть конкретным относительно того, какие значения по умолчанию «null» используются для его свойств:

var c =
    from a in alpha
    join b in beta on b.field1 equals a.field1 into b_temp
    from b_value in b_temp.DefaultIfEmpty( new MyObjectB { Field1 = String.Empty, Field2 = (DateTime?) null })
    select new { Alpha = a, Beta_field1 = b_value.Field1, Beta_field2 = b_value.Field2 };

Это гарантирует, что само «b» не является нулевым (но его свойства могут быть нулевыми, используя заданные вами значения по умолчанию), и это позволяет вам проверять свойства b_value без получения исключения нулевой ссылки для b_value. Обратите внимание, что для обнуляемого DateTime тип (DateTime?), То есть «обнуляемый DateTime» должен быть указан как «Тип» нулевого значения в спецификации для «DefaultIfEmpty» (это также будет применяться к типам, которые не являются «изначально»). обнуляемый, например, double, float).

Вы можете выполнить несколько левых внешних объединений, просто связав приведенный выше синтаксис.

Крис Хэлкроу
источник
1
откуда берется b_value?
Джек Фрейзер
9

Вот пример, если вам нужно объединить более двух таблиц:

from d in context.dc_tpatient_bookingd
join bookingm in context.dc_tpatient_bookingm 
     on d.bookingid equals bookingm.bookingid into bookingmGroup
from m in bookingmGroup.DefaultIfEmpty()
join patient in dc_tpatient
     on m.prid equals patient.prid into patientGroup
from p in patientGroup.DefaultIfEmpty()

Ссылка: https://stackoverflow.com/a/17142392/2343

Самир Алибхай
источник
4

Метод расширения, который работает как левое соединение с синтаксисом соединения

public static class LinQExtensions
{
    public static IEnumerable<TResult> LeftJoin<TOuter, TInner, TKey, TResult>(
        this IEnumerable<TOuter> outer, IEnumerable<TInner> inner, 
        Func<TOuter, TKey> outerKeySelector, 
        Func<TInner, TKey> innerKeySelector, 
        Func<TOuter, TInner, TResult> resultSelector)
    {
        return outer.GroupJoin(
            inner, 
            outerKeySelector, 
            innerKeySelector,
            (outerElement, innerElements) => resultSelector(outerElement, innerElements.FirstOrDefault()));
    }
}

только что написал это в ядре .NET, и, кажется, работает как положено.

Небольшой тест:

        var Ids = new List<int> { 1, 2, 3, 4};
        var items = new List<Tuple<int, string>>
        {
            new Tuple<int, string>(1,"a"),
            new Tuple<int, string>(2,"b"),
            new Tuple<int, string>(4,"d"),
            new Tuple<int, string>(5,"e"),
        };

        var result = Ids.LeftJoin(
            items,
            id => id,
            item => item.Item1,
            (id, item) => item ?? new Tuple<int, string>(id, "not found"));

        result.ToList()
        Count = 4
        [0]: {(1, a)}
        [1]: {(2, b)}
        [2]: {(3, not found)}
        [3]: {(4, d)}
Bezio
источник
4

Вот довольно простая для понимания версия с использованием синтаксиса метода:

IEnumerable<JoinPair> outerLeft =
    lefts.SelectMany(l => 
        rights.Where(r => l.Key == r.Key)
              .DefaultIfEmpty(new Item())
              .Select(r => new JoinPair { LeftId = l.Id, RightId = r.Id }));
Тим Полманн
источник
3

Существует три таблицы: people, школы и people_schools, которые связывают людей со школами, в которых они учатся. Ссылка на человека с id = 6 отсутствует в таблице people_schools. Однако человек с id = 6 представлен в итоговой таблице.

List<Person> persons = new List<Person>
{
    new Person { id = 1, name = "Alex", phone = "4235234" },
    new Person { id = 2, name = "Bob", phone = "0014352" },
    new Person { id = 3, name = "Sam", phone = "1345" },
    new Person { id = 4, name = "Den", phone = "3453452" },
    new Person { id = 5, name = "Alen", phone = "0353012" },
    new Person { id = 6, name = "Simon", phone = "0353012" }
};

List<School> schools = new List<School>
{
    new School { id = 1, name = "Saint. John's school"},
    new School { id = 2, name = "Public School 200"},
    new School { id = 3, name = "Public School 203"}
};

List<PersonSchool> persons_schools = new List<PersonSchool>
{
    new PersonSchool{id_person = 1, id_school = 1},
    new PersonSchool{id_person = 2, id_school = 2},
    new PersonSchool{id_person = 3, id_school = 3},
    new PersonSchool{id_person = 4, id_school = 1},
    new PersonSchool{id_person = 5, id_school = 2}
    //a relation to the person with id=6 is absent
};

var query = from person in persons
            join person_school in persons_schools on person.id equals person_school.id_person
            into persons_schools_joined
            from person_school_joined in persons_schools_joined.DefaultIfEmpty()
            from school in schools.Where(var_school => person_school_joined == null ? false : var_school.id == person_school_joined.id_school).DefaultIfEmpty()
            select new { Person = person.name, School = school == null ? String.Empty : school.name };

foreach (var elem in query)
{
    System.Console.WriteLine("{0},{1}", elem.Person, elem.School);
}
Alex
источник
Хотя это, возможно, ответ на вопрос, предоставьте некоторое объяснение вашего ответа :)
Амир
2

Это синтаксис SQL по сравнению с синтаксисом LINQ для внутренних и левых внешних объединений. Левое внешнее соединение:

http://www.ozkary.com/2011/07/linq-to-entity-inner-and-left-joins.html

«В следующем примере выполняется групповое объединение между продуктом и категорией. По сути, это левое соединение. Выражение into возвращает данные, даже если таблица категорий пуста. Чтобы получить доступ к свойствам таблицы категорий, мы должны теперь выбрать из перечисляемого результата добавив from cl в оператор catList.DefaultIfEmpty ().

ozkary
источник
1

Выполнить левые внешние объединения в linq C # // Выполнить левые внешние объединения

class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

class Child
{
    public string Name { get; set; }
    public Person Owner { get; set; }
}
public class JoinTest
{
    public static void LeftOuterJoinExample()
    {
        Person magnus = new Person { FirstName = "Magnus", LastName = "Hedlund" };
        Person terry = new Person { FirstName = "Terry", LastName = "Adams" };
        Person charlotte = new Person { FirstName = "Charlotte", LastName = "Weiss" };
        Person arlene = new Person { FirstName = "Arlene", LastName = "Huff" };

        Child barley = new Child { Name = "Barley", Owner = terry };
        Child boots = new Child { Name = "Boots", Owner = terry };
        Child whiskers = new Child { Name = "Whiskers", Owner = charlotte };
        Child bluemoon = new Child { Name = "Blue Moon", Owner = terry };
        Child daisy = new Child { Name = "Daisy", Owner = magnus };

        // Create two lists.
        List<Person> people = new List<Person> { magnus, terry, charlotte, arlene };
        List<Child> childs = new List<Child> { barley, boots, whiskers, bluemoon, daisy };

        var query = from person in people
                    join child in childs
                    on person equals child.Owner into gj
                    from subpet in gj.DefaultIfEmpty()
                    select new
                    {
                        person.FirstName,
                        ChildName = subpet!=null? subpet.Name:"No Child"
                    };
                       // PetName = subpet?.Name ?? String.Empty };

        foreach (var v in query)
        {
            Console.WriteLine($"{v.FirstName + ":",-25}{v.ChildName}");
        }
    }

    // This code produces the following output:
    //
    // Magnus:        Daisy
    // Terry:         Barley
    // Terry:         Boots
    // Terry:         Blue Moon
    // Charlotte:     Whiskers
    // Arlene:        No Child

https://dotnetwithhamid.blogspot.in/

Хамид
источник
1

Я хотел бы добавить, что если вы получите расширение MoreLinq, теперь есть поддержка как гомогенных, так и гетерогенных левых соединений.

http://morelinq.github.io/2.8/ref/api/html/Overload_MoreLinq_MoreEnumerable_LeftJoin.htm

пример:

//Pretend a ClientCompany object and an Employee object both have a ClientCompanyID key on them

return DataContext.ClientCompany
    .LeftJoin(DataContext.Employees,                         //Table being joined
        company => company.ClientCompanyID,                  //First key
        employee => employee.ClientCompanyID,                //Second Key
        company => new {company, employee = (Employee)null}, //Result selector when there isn't a match
        (company, employee) => new { company, employee });   //Result selector when there is a match

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

Оглядываясь назад, это может сработать, но оно преобразует IQueryable в IEnumerable, так как morelinq не преобразует запрос в SQL.

Вместо этого вы можете использовать GroupJoin, как описано здесь: https://stackoverflow.com/a/24273804/4251433

Это гарантирует, что он останется IQueryable на случай, если вам потребуется выполнить дальнейшие логические операции над ним позже.

Риз Де Винд
источник
1

Простой способ - использовать ключевое слово Let. Это работает для меня.

from AItem in Db.A
Let BItem = Db.B.Where(x => x.id == AItem.id ).FirstOrDefault() 
Where SomeCondition
Select new YourViewModel
{
    X1 = AItem.a,
    X2 = AItem.b,
    X3 = BItem.c
}

Это симуляция левого соединения. Если каждый элемент в таблице B не соответствует элементу A, BItem возвращает ноль

Махди Могими
источник
0

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

В этом случае, если я сделаю это в условии соединения, я уменьшу количество возвращаемых строк.

Тройное состояние используется (= n == null ? "__" : n.MonDayNote,)

  • Если объект null(так что нет совпадения), то вернуть то, что после ?. __, в этом случае.

  • В противном случае, вернуть то , что после того :, n.MonDayNote.

Спасибо другим авторам, с которых я начал свою собственную проблему.


        var schedLocations = (from f in db.RAMS_REVENUE_LOCATIONS
              join n in db.RAMS_LOCATION_PLANNED_MANNING on f.revenueCenterID equals

                  n.revenueCenterID into lm

              from n in lm.DefaultIfEmpty()

              join r in db.RAMS_LOCATION_SCHED_NOTE on f.revenueCenterID equals r.revenueCenterID
              into locnotes

              from r in locnotes.DefaultIfEmpty()
              where f.LocID == nLocID && f.In_Use == true && f.revenueCenterID > 1000

              orderby f.Areano ascending, f.Locname ascending
              select new
              {
                  Facname = f.Locname,
                  f.Areano,
                  f.revenueCenterID,
                  f.Locabbrev,

                  //  MonNote = n == null ? "__" : n.MonDayNote,
                  MonNote = n == null ? "__" : n.MonDayNote,
                  TueNote = n == null ? "__" : n.TueDayNote,
                  WedNote = n == null ? "__" : n.WedDayNote,
                  ThuNote = n == null ? "__" : n.ThuDayNote,

                  FriNote = n == null ? "__" : n.FriDayNote,
                  SatNote = n == null ? "__" : n.SatDayNote,
                  SunNote = n == null ? "__" : n.SunDayNote,
                  MonEmpNbr = n == null ? 0 : n.MonEmpNbr,
                  TueEmpNbr = n == null ? 0 : n.TueEmpNbr,
                  WedEmpNbr = n == null ? 0 : n.WedEmpNbr,
                  ThuEmpNbr = n == null ? 0 : n.ThuEmpNbr,
                  FriEmpNbr = n == null ? 0 : n.FriEmpNbr,
                  SatEmpNbr = n == null ? 0 : n.SatEmpNbr,
                  SunEmpNbr = n == null ? 0 : n.SunEmpNbr,
                  SchedMondayDate = n == null ? dMon : n.MondaySchedDate,
                  LocNotes = r == null ? "Notes: N/A" : r.LocationNote

              }).ToList();
                Func<int, string> LambdaManning = (x) => { return x == 0 ? "" : "Manning:" + x.ToString(); };
        DataTable dt_ScheduleMaster = PsuedoSchedule.Tables["ScheduleMasterWithNotes"];
        var schedLocations2 = schedLocations.Where(x => x.SchedMondayDate == dMon);
BionicCyborg
источник
0
class Program
{
    List<Employee> listOfEmp = new List<Employee>();
    List<Department> listOfDepart = new List<Department>();

    public Program()
    {
        listOfDepart = new List<Department>(){
            new Department { Id = 1, DeptName = "DEV" },
            new Department { Id = 2, DeptName = "QA" },
            new Department { Id = 3, DeptName = "BUILD" },
            new Department { Id = 4, DeptName = "SIT" }
        };


        listOfEmp = new List<Employee>(){
            new Employee { Empid = 1, Name = "Manikandan",DepartmentId=1 },
            new Employee { Empid = 2, Name = "Manoj" ,DepartmentId=1},
            new Employee { Empid = 3, Name = "Yokesh" ,DepartmentId=0},
            new Employee { Empid = 3, Name = "Purusotham",DepartmentId=0}
        };

    }
    static void Main(string[] args)
    {
        Program ob = new Program();
        ob.LeftJoin();
        Console.ReadLine();
    }

    private void LeftJoin()
    {
        listOfEmp.GroupJoin(listOfDepart.DefaultIfEmpty(), x => x.DepartmentId, y => y.Id, (x, y) => new { EmpId = x.Empid, EmpName = x.Name, Dpt = y.FirstOrDefault() != null ? y.FirstOrDefault().DeptName : null }).ToList().ForEach
            (z =>
            {
                Console.WriteLine("Empid:{0} EmpName:{1} Dept:{2}", z.EmpId, z.EmpName, z.Dpt);
            });
    }
}

class Employee
{
    public int Empid { get; set; }
    public string Name { get; set; }
    public int DepartmentId { get; set; }
}

class Department
{
    public int Id { get; set; }
    public string DeptName { get; set; }
}

ВЫВОД

Маникандан Дейвасигамани
источник
0

Согласно моему ответу на подобный вопрос, здесь:

Linq to SQL оставил внешнее объединение с использованием синтаксиса Lambda и объединение в 2 столбца (составной ключ объединения)

Получить код здесь или клонировать мой репозиторий Github , и играть!

Запрос:

        var petOwners =
            from person in People
            join pet in Pets
            on new
            {
                person.Id,
                person.Age,
            }
            equals new
            {
                pet.Id,
                Age = pet.Age * 2, // owner is twice age of pet
            }
            into pets
            from pet in pets.DefaultIfEmpty()
            select new PetOwner
            {
                Person = person,
                Pet = pet,
            };

Lambda:

        var petOwners = People.GroupJoin(
            Pets,
            person => new { person.Id, person.Age },
            pet => new { pet.Id, Age = pet.Age * 2 },
            (person, pet) => new
            {
                Person = person,
                Pets = pet,
            }).SelectMany(
            pet => pet.Pets.DefaultIfEmpty(),
            (people, pet) => new
            {
                people.Person,
                Pet = pet,
            });
Адам Кокс
источник
0

Обзор: в этом фрагменте кода я демонстрирую, как группировать по идентификатору, где у таблиц 1 и 2 есть отношения один ко многим. Я группируюсь по Id, Field1 и Field2. Подзапрос полезен, если требуется третий просмотр таблицы, и для этого потребовалось бы левое соединение. Я показываю левую группу соединений и подзапрос linq. Результаты эквивалентны.

class MyView
{
public integer Id {get,set};
    public String Field1  {get;set;}
public String Field2 {get;set;}
    public String SubQueryName {get;set;}                           
}

IList<MyView> list = await (from ci in _dbContext.Table1
                                               join cii in _dbContext.Table2
                                                   on ci.Id equals cii.Id

                                               where ci.Field1 == criterion
                                               group new
                                               {
                                                   ci.Id
                                               } by new { ci.Id, cii.Field1, ci.Field2}

                                           into pg
                                               select new MyView
                                               {
                                                   Id = pg.Key.Id,
                                                   Field1 = pg.Key.Field1,
                                                   Field2 = pg.Key.Field2,
                                                   SubQueryName=
                                                   (from chv in _dbContext.Table3 where chv.Id==pg.Key.Id select chv.Field1).FirstOrDefault()
                                               }).ToListAsync<MyView>();


 Compared to using a Left Join and Group new

IList<MyView> list = await (from ci in _dbContext.Table1
                                               join cii in _dbContext.Table2
                                                   on ci.Id equals cii.Id

                       join chv in _dbContext.Table3
                                                  on cii.Id equals chv.Id into lf_chv
                                                from chv in lf_chv.DefaultIfEmpty()

                                               where ci.Field1 == criterion
                                               group new
                                               {
                                                   ci.Id
                                               } by new { ci.Id, cii.Field1, ci.Field2, chv.FieldValue}

                                           into pg
                                               select new MyView
                                               {
                                                   Id = pg.Key.Id,
                                                   Field1 = pg.Key.Field1,
                                                   Field2 = pg.Key.Field2,
                                                   SubQueryName=pg.Key.FieldValue
                                               }).ToListAsync<MyView>();
Золотой Лев
источник
-1
(from a in db.Assignments
     join b in db.Deliveryboys on a.AssignTo equals b.EmployeeId  

     //from d in eGroup.DefaultIfEmpty()
     join  c in  db.Deliveryboys on a.DeliverTo equals c.EmployeeId into eGroup2
     from e in eGroup2.DefaultIfEmpty()
     where (a.Collected == false)
     select new
     {
         OrderId = a.OrderId,
         DeliveryBoyID = a.AssignTo,
         AssignedBoyName = b.Name,
         Assigndate = a.Assigndate,
         Collected = a.Collected,
         CollectedDate = a.CollectedDate,
         CollectionBagNo = a.CollectionBagNo,
         DeliverTo = e == null ? "Null" : e.Name,
         DeliverDate = a.DeliverDate,
         DeliverBagNo = a.DeliverBagNo,
         Delivered = a.Delivered

     });
Шактипрасад Суэйн
источник
-1

Простое решение для LEFT OUTER JOIN :

var setA = context.SetA;
var setB = context.SetB.Select(st=>st.Id).Distinct().ToList();
var leftOuter  = setA.Where(stA=> !setB.Contains(stA.Id)); 

примечания :

  • Для повышения производительности SetB может быть преобразован в словарь (если это сделано, то вы должны изменить это :! SetB.Contains (stA.Id) ) или HashSet
  • Когда задействовано более одного поля, этого можно достичь с помощью операций Set и класса, который реализует: IEqualityComparer
Энрике Рамос
источник
Левое внешнее соединение вернет совпадение setAи setBответ.
NetMage