ВЕРНУТЬСЯ ВЕРСИЯ В LINQ

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

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

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

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

где JoinPair – это class:

 public class JoinPair { long leftId; long rightId; } 

    19 Solutions collect form web for “ВЕРНУТЬСЯ ВЕРСИЯ В LINQ”

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

    101 Образцы LINQ – левое внешнее соединение

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

    Necromancing.
    Если используется поставщик 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() 

    Этот синтаксис очень запутан, и непонятно, как это работает, когда вы хотите оставить в таблице несколько МНОЖЕСТВ.

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

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

     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 он отлично переведёт на следующий очень parsingчивый 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 } ); 

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

     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 }) 

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

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

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

     public static class LinqExt { public static IEnumerable LeftOuterJoin(this IEnumerable left, IEnumerable right, Func leftKey, Func rightKey, Func result) { return left.GroupJoin(right, leftKey, rightKey, (l, r) => new { l, r }) .SelectMany( o => orDefaultIfEmpty(), (l, r) => new { lft= ll, 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 }) 

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

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

     public static IEnumerable LeftJoin( this IEnumerable outer, IEnumerable inner , Func outerKeySelector, Func innerKeySelector , Func resultSelector, IEqualityComparer 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.Default); } static IEnumerable LeftJoinImpl( IEnumerable outer, IEnumerable inner , Func outerKeySelector, Func innerKeySelector , Func resultSelector, IEqualityComparer 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)); } 

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

     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() 

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

    Теперь, если правая сторона является результатом отдельного запроса LINQ, он будет состоять из анонимных типов, которые могут быть либо «что-то», либо «null». Однако, если это перечислимо (например, List – где MyObjectB – это class с 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» не является нулевым (но его свойства могут быть нулевыми, используя заданные по умолчанию значения null, которые вы указали), и это позволяет вам проверять свойства b_value, не получая исключение ссылочной ссылки для b_value. Обратите внимание, что для нулевой даты DateTime тип (DateTime?), То есть «nullable DateTime», должен быть указан как «Тип» нулевого значения в спецификации для «DefaultIfEmpty» (это также относится к типам, которые не являются «изначально» ‘nullable, например double, float).

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

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

     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

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

     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 people = new List {magnus, terry, charlotte, arlene}; List pets = new List {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 #)

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

     List persons = new List { 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 schools = new List { 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 persons_schools = new List { 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); } 

    Это синтаксис SQL, сравниваемый с синтаксисом LINQ для внутреннего и левого внешних соединений. Left Outer Join:

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

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

    Выполнять левые внешние соединения в 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 people = new List { magnus, terry, charlotte, arlene }; List childs = new List { 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/

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

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

    Тернарное условие используется (= 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 LambdaManning = (x) => { return x == 0 ? "" : "Manning:" + x.ToString(); }; DataTable dt_ScheduleMaster = PsuedoSchedule.Tables["ScheduleMasterWithNotes"]; var schedLocations2 = schedLocations.Where(x => x.SchedMondayDate == dMon); 

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

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

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

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

      var Ids = new List { 1, 2, 3, 4}; var items = new List> { new Tuple(1,"a"), new Tuple(2,"b"), new Tuple(4,"d"), new Tuple(5,"e"), }; var result = Ids.LeftJoin( items, id => id, item => item.Item1, (id, item) => item ?? new Tuple(id, "not found")); result.ToList() Count = 4 [0]: {(1, a)} [1]: {(2, b)} [2]: {(3, not found)} [3]: {(4, d)} 

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

     IEnumerable outerLeft = lefts.SelectMany(l => rights.Where(r => l.Key == r.Key) .DefaultIfEmpty(new Item()) .Select(r => new JoinPair { LeftId = l.Id, RightId = r.Id })); 
     (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 }); 
     class Program { List listOfEmp = new List(); List listOfDepart = new List(); public Program() { listOfDepart = new List(){ 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(){ 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; } } 

    ВЫВОД

    Простое решение для 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 и classа, которые реализуют: IEqualityComparer

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

    Interesting Posts

    .NET 2.0 «не установлен» после обновления до Windows 7

    Регистрация регистрации сетевого интерфейса Windows через GPO (автоматизация).

    Win 7 – Скрытые свойства управления питанием и странная производительность

    Что такое ярлык клавиатуры для перехода на последнее сообщение в Mac OS X Mail.app?

    Какое другое приложение использует мою звуковую карту?

    Фиксация сломанной «цепи доверия» в сертификатах Windows 7?

    Заполните в Excel, но на основе нескольких значений

    Завершение работы Windows 7 с обратным отсчетом

    Сохранение изменений разрешения для устройства

    Проверка целостности файла, который был перенесен с ненадежной среды

    Неопределяемая графическая карта

    Как восстановить функциональность Zip-файла Windows по умолчанию?

    Windows 7 RTM: Loopback SMB через соединение TCP / IPv6, предотвращающий спящий режим

    В каких файлах просматривается диск обновления Windows 7?

    Как определить, сколько оперативной памяти мне нужно?

    Давайте будем гением компьютера.