关于C#:在linq中左外部联接

LEFT OUTER JOIN in LINQ

如何在不使用join-on-equals-into子句的情况下执行c linq to objects中的左外部联接?有没有办法用where条款做到这一点?纠正问题:因为内部连接很容易,我有这样的解决方案

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

但是对于左外部连接,我需要一个解决方案。我的有点像这样,但不起作用

1
2
3
4
5
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是类:

1
public class JoinPair { long leftId; long rightId; }


如:

101个Linq示例-左外部联接

1
2
3
4
5
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 };


如果使用数据库驱动的LINQ提供程序,则可以编写一个可读性更高的左外部联接,如下所示:

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

如果省略DefaultIfEmpty(),则会有一个内部连接。

接受接受的答案:

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

这个语法非常混乱,当您想离开join多个表时,它是如何工作的还不清楚。< BR>


应该注意的是,from alias in Repo.whatever.Where(condition).DefaultIfEmpty()与外部应用程序/左侧联接侧边相同,只要不引入每行值(即实际的外部应用程序),任何(合适的)数据库优化器都完全能够将其转换为左侧联接。在LINQ-2对象中不要这样做(因为使用LINQ-to对象时没有DB优化器)。

详细示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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();

当与Linq2 SQL一起使用时,它将很好地转换为以下非常清晰的SQL查询:

1
2
3
4
5
6
7
8
9
10
11
12
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会正确地将其转换为join操作,但是对于对象,此方法强制进行完全扫描,并且不利用索引搜索,为什么…):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    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表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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  
  })


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

1
2
3
4
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 };


Now as an extension method:

1
2
3
4
5
6
7
8
9
10
11
12
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一样使用:

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

希望这能节省你一些时间。


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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));
     }
   }

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

1
2
3
4
5
6
7
8
9
10
11
   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));
   }


看看这个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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,即使他在右边没有匹配项。

这是参考资料

如何:执行左侧外部连接(C编程指南)


这是一般形式(如其他答案中所述)

1
2
3
4
5
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 };

不过,这里有一个解释,我希望能澄清这到底意味着什么!

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

基本上创建一个单独的结果集b_temp,它有效地为右侧的条目(b'中的条目)包含空的"行"。

然后下一行:

1
from b_value in b_temp.DefaultIfEmpty()

..迭代该结果集,为右侧的"row"设置默认的空值,并将右侧行join的结果设置为"b_value"的值(即,如果有匹配的记录,则为右侧的值,否则为"null")。

现在,如果右侧是单独的LINQ查询的结果,它将由匿名类型组成,匿名类型只能是"something"或"null"。但是,如果它是一个可枚举的(例如,一个列表-其中myObjectB是一个具有2个字段的类),那么可以具体说明其属性使用的默认"空"值是什么:

1
2
3
4
5
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_值的属性,而不必为b_值获取空引用异常。请注意,对于可为空的日期时间,类型为(日期时间?)例如,"nullable datetime"必须在"defaultifempty"的规范中指定为空的"type"(这也适用于不可"native"为空的类型,例如double、float)。

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


下面是一个例子,如果您需要连接两个以上的表:

1
2
3
4
5
6
7
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


有三张表:人、学校和人-学校,将人与他们学习的学校联系起来。表中没有对ID=6的人的引用。但是,id=6的人出现在结果lef-joined网格中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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
<div class="
suo-content">[collapse title=""]<ul><li>虽然这可能是问题的答案,但请对您的答案提供一些解释:)</li></ul>[/collapse]</div><hr><P>扩展方法的工作方式与使用联接语法的左联接类似</P>[cc lang="csharp"]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核心中编写了它,它似乎按预期工作。

小测验:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
        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)}


这是一个与内部和左侧外部联接的LINQ语法比较的SQL语法。左外部联接:

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

"下面的示例是在产品和类别之间进行组联接。这基本上是左连接。即使类别表为空,into表达式也返回数据。要访问类别表的属性,现在必须通过在catlist.defaultifempty()语句中添加fromcl从可枚举结果中进行选择。


在LINQ C中执行左外部联接#//执行左外部联接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
2
3
4
5
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 }));

我想补充一点,如果您得到morelinq扩展,现在就可以同时支持同质和异构的左连接了

http://morelinq.github.io/2.8/ref/api/html/overload_morelinq_moreenumerable_leftjoin.htm(http://morelinq.github.io/2.8/ref/api/html/overload_morelinq_moreenumerable

例子:

1
2
3
4
5
6
7
8
//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保持,以防您以后需要对其进行进一步的逻辑操作。


如果需要联接和过滤某些内容,可以在联接之外进行。可以在创建集合后进行筛选。

在本例中,如果在联接条件中执行此操作,则会减少返回的行。

采用三元条件dOCx1〔0〕。

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

  • 否则,返回:n.MonDayNote之后的内容。

感谢其他的贡献者,这是我开始自己的问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
        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);


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
(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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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; }
}

产量


左外部联接的简单解决方案:

1
2
3
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))或哈希集
  • 当涉及多个字段时,可以使用set操作和实现以下内容的类来实现:ieQualityComparer