ChatGPT解决这个技术问题 Extra ChatGPT

LINQ中的左外连接

如何在不使用 join-on-equals-into 子句的情况下在 C# LINQ 中对对象执行左外连接?有没有办法用 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; }
你能举个例子说明你想要达到的目标吗?
正常的左外连接是这样的: var a = from b in bb join c in cc on b.bbbbb 等于 c.ccccc into dd from d in dd.DefaultIfEmpty() select b.sss;我的问题是没有任何方法可以使用 join-on-equals-into 子句来做到这一点,例如 var a = from b in bb from c in cc where b.bbb == c.cccc ... 等等.. .
肯定有,但你应该发布一个你已经拥有的代码示例,以便人们可以给你一个更好的答案
我正在寻找一个 "Left excluding" JOIN(我将它与“OUTER”的概念混淆了)。 This answer was closer to what I wanted.

I
Ian Kemp

"Perform left outer joins" 所述:

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

我正在尝试同样的事情,但在连接运算符上出现错误,它显示“连接子句中的一个表达式的类型不正确”。
@jain 如果您的类型不同,则连接将不起作用。您的密钥很可能是不同的数据类型。例如,两个键都是 int 吗?
贾恩有什么解决办法?我也面临同样的错误,在我的情况下类型也相同。
现在我们可以使用像 select new { Category = c, ProductName = p.ProductName ?? "(No products)" }; 这样的空值合并
Linq 与 lambda 的等价物是什么?
j
jpmc26

如果使用数据库驱动的 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() 与外部应用/左连接横向相同,任何(体面的)数据库优化器都可以完美地转换为左连接加入,只要您不引入每行值(也就是实际的外部应用)。不要在 Linq-2-Objects 中执行此操作(因为使用 Linq-to-Objects 时没有 DB-optimizer)。

详细示例

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

编辑:

另请参阅“Convert SQL Server query to Linq query”以获得更复杂的示例。

此外,如果您在 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
    }

);

这个答案实际上很有帮助。感谢您实际提供可以理解的语法。
WTB 一个 NHibernate 兼容的 LINQ 查询... :)
LINQ to SQL 将其正确地转换为连接操作。然而,在对象上,这种方法会强制进行全面扫描,这就是为什么官方文档提供了可以利用散列来索引搜索的组加入解决方案。
我认为显式 join 的语法比 where 后跟 DefaultIfEmpty 的语法更具可读性和清晰性
@user3441905:只要您只需要将表 a 与表 b 连接起来,就可以了。但一旦你拥有更多,它就不会了。但即使只有 2 张桌子,我也认为它过于冗长。流行的观点似乎也反对你,因为当最佳答案已经有 90 多个赞成票时,这个答案从 0 开始。
F
Fereydoon Barikzehy

使用 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   
  });

Join 和 GroupJoin 都不真正支持左连接。使用 GroupJoin 的诀窍是您可以拥有空组,然后将这些空组转换为空值。 DefaultIfEmpty 只是这样做,这意味着 Enumerable.Empty<Product>.DefaultIfEmpty() 将返回一个具有单个值 default(Product) 的 IEnumerable。
所有这些都是为了执行左连接??
谢谢你!那里没有太多的 lambda 表达式示例,这对我有用。
感谢你的回答。它产生了与我多年来编写的原始 SQL LEFT OUTER JOIN 最接近的东西
不需要最后一个 Select(),SelectMany() 中的 anon obj 可以重构为相同的输出。另一个想法是测试 y 是否为空,以模拟更接近的 LEFT JOIN 等价。
M
Matas Vaitkevicius

现在作为扩展方法:

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

希望这可以节省您一些时间。


这很好,但是在您的示例中,如果 list 包含键 list2 不会因为 r 将是 null 而引发异常,r.people 不会抛出异常吗?不应该是r?.people吗?否则,它只是一个也会引发异常的内部连接。或者,我认为您可以将“默认右元素”参数添加到 LeftOuterJoin() 并将其传递给 DefaultIfEmpty()
这不应该是 IQueryable 吗?
N
Nielsvh

看看这个example。此查询应该有效:

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

使用 join into 后是否可以在 select 子句中访问 r
@FarhadAlizadehNoori 是的,可以。
作者可能打算在第二个 from 子句中重用 r。即from r in lrs.DefaultIfEmpty() 否则此查询没有多大意义,甚至可能由于r 与选择的上下文无关而无法编译。
@Devart,当我阅读您的查询时,它让我想起了约翰·克莱斯 (John Cleese) 的电影 Clockwise,哈哈。
在 leftRights 中从左到右到左到右到左到右……哦,天哪……在 LINQ 中使用 LEFT OUTER JOIN 的语法确实不清楚,但是这些名称确实使它更加不清楚。
q
quadroid

通过扩展方法实现左外连接可能看起来像

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

然后结果选择器必须处理空元素。外汇。

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

然而,这只是 LINQ to 对象的一个选项,并且无法将查询转换为任何查询提供程序,这是此操作最常见的用例。
但问题是“如何在 C# LINQ to objects 中执行左外连接......”
LeftJoin 方法中的最后一个参数“比较器”应该是可选参数等于 null 我猜
B
Basheer AL-MOMANI

看看这个例子

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:

现在即使该元素 has no matches in the right,您也可以 include elements from the left,在我们的例子中,我们检索了 Arlene,即使他在右边没有匹配

这是参考

How to: Perform Left Outer Joins (C# Programming Guide)


输出应该是:阿琳:不存在
C
Chris Halcrow

这是一般形式(已在其他答案中提供)

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 查询的结果,它将由匿名类型组成,只能是“某物”或“空”。但是,如果它是可枚举的(例如 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”的规范中指定为 null 的“类型”(这也适用于不是“本机”的类型' 可以为空,例如 double、float)。

您可以通过简单地链接上述语法来执行多个左外连接。


b_value 来自哪里?
C
Community

如果您需要加入 2 个以上的表,以下是一个示例:

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


T
Tim Pohlmann

这是一个使用方法语法的相当容易理解的版本:

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

有趣的是如何更简单地避免 LINQ 函数在其名称中包含单词“join”
B
Bezio

扩展方法,类似于使用 Join 语法的左连接

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

R
Reese De Wind

我想补充一点,如果您获得了 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由于该问题专门要求“LINQ to objects”而不是“LINQ to SQL”等的解决方案,因此这是最好的答案(不是重新发明轮子)。另外,如果左侧集合中存在项目但右侧集合中不存在项目,则此处的许多答案实际上会引发异常,这意味着它们根本不是左连接。它们只是内部连接,顶部散布着毫无意义的异常。
G
Gilad Green

共有三个表:persons、schools 和 persons_schools,将人员与他们就读的学校联系起来。persons_schools 表中没有对 id=6 的人员的引用。然而,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);
}

虽然这可能是问题的答案,但请提供一些关于您的答案的解释:)
m
mahdi moghimi

简单的方法是使用 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
}

这是Left Join的模拟。如果 B 表中的每个项目都与 A 项目不匹配,则 BItem 返回 null


请注意,仅当您希望左侧输入列表中的每个项目在输出列表中恰好有 1 个项目时,这才有意义。如果正确的列表有重复,它们将通过 FirstOrDefault() 丢弃。所以这不是一个真正的左连接。但是,对于从具有唯一键的查找中检索数据等常见情况,它仍然很有用。
o
ozkary

这是一种 SQL 语法,与用于内连接和左外连接的 LINQ 语法相比。左外连接:

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

“下面的例子在产品和类别之间进行组连接。这本质上是左连接。即使类别表为空,into 表达式也会返回数据。要访问类别表的属性,我们现在必须从可枚举的结果中进行选择通过在 catList.DefaultIfEmpty() 语句中添加 from cl。


A
Adam Cox

根据我对类似问题的回答,在这里:

Linq to SQL left outer join using Lambda syntax and joining on 2 columns (composite join key)

获取 code here,或克隆 my github repo,然后开始游戏!

询问:

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

拉姆达:

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

H
Hamid

在 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/


J
Josef

这是使用 IQueryable 而不是 IEnumerable 的扩展方法解决方案的一个版本

public class OuterJoinResult<TLeft, TRight>
{
    public TLeft LeftValue { get; set; }
    public TRight RightValue { get; set; }
}

public static IQueryable<TResult> LeftOuterJoin<TLeft, TRight, TKey, TResult>(this IQueryable<TLeft> left, IQueryable<TRight> right, Expression<Func<TLeft, TKey>> leftKey, Expression<Func<TRight, TKey>> rightKey, Expression<Func<OuterJoinResult<TLeft, TRight>, TResult>> result)
{
    return left.GroupJoin(right, leftKey, rightKey, (l, r) => new { l, r })
          .SelectMany(o => o.r.DefaultIfEmpty(), (l, r) => new OuterJoinResult<TLeft, TRight> { LeftValue = l.l, RightValue = r })
          .Select(result);
}

与扩展方法一样,调用方法变得比自己使用基本的 LINQ 方法更复杂。在代码中调用 GroupJoin 一点也不难,尤其是。不使用查询语法。
R
Regular Jo

如果您需要加入并过滤某些内容,可以在加入之外完成。可以在创建集合后进行过滤。

在这种情况下,如果我在连接条件中执行此操作,我会减少返回的行。

使用三元条件 (= n == null ? "__" : n.MonDayNote,)

如果对象为空(因此不匹配),则返回 ? 之后的内容。 __, 在这种情况下。

否则,返回 :, 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);

M
Manikandan Deivasigamani
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; }
}

OUTPUT


G
Golden Lion

概述:在此代码段中,我演示了如何在 Table1 和 Table2 具有一对多关系的情况下按 ID 进行分组。我对 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>();

N
Nikolay Kostov
(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

     });