C# Linq中的outer join

栏目: ASP.NET · 发布时间: 5年前

内容简介:昨天用EF查数据库,遇到了个很奇怪的问题。linq里并没有left join关键字,也没有LeftJoin的方法,所有join默认都是inner join。那么如果我们想以左为基,不管右侧是否存在都可以查询出一条记录,应该怎么做的?建数据库太麻烦,这里咱么用集合代替,但是具体写法是一样的。首先是租客类

昨天用EF查数据库,遇到了个很奇怪的问题。linq里并没有left join关键字,也没有LeftJoin的方法,所有join默认都是inner join。那么如果我们想以左为基,不管右侧是否存在都可以查询出一条记录,应该怎么做的?

建数据库太麻烦,这里咱么用集合代替,但是具体写法是一样的。

首先是租客类

public class People
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int HouseId { get; set; }
}

然后是房屋类

public class House
{
    public int Id { get; set; }
    public string Name { get; set; }
}

然后是Service

public class Service
{
    public IEnumerable<People> People => GetAllPeople();
    public IEnumerable<House> Houses => GetAllHouse();

    private IEnumerable<People> GetAllPeople()
    {
        var list = new List<People>();

        list.Add(new People() { Id = 1, Name = "张三", HouseId = 0 });
        list.Add(new People() { Id = 2, Name = "李四", HouseId = 1 });
        list.Add(new People() { Id = 3, Name = "王五", HouseId = 2 });
        list.Add(new People() { Id = 4, Name = "赵六", HouseId = 3 });
        list.Add(new People() { Id = 5, Name = "孙七", HouseId = 4 });
        list.Add(new People() { Id = 6, Name = "周八", HouseId = 0 });
        list.Add(new People() { Id = 7, Name = "吴九", HouseId = 0 });
        list.Add(new People() { Id = 8, Name = "郑十", HouseId = 0 });

        return list;
    }
  
    private IEnumerable<House> GetAllHouse()
    {
        var list = new List<House>();

        for (int i = 1; i < 9; i++)
        {
            list.Add(new House() { Id = i, Name = i + "号公寓" });
        }

        return list;
    }
}

万事俱备,接下来开始正题:

var innerjoin = from people in service.People
                join house in service.Houses
                on people.HouseId equals house.Id
                select new { people.Id, people.Name, HouseName = house.Name };

foreach(var item in innerjoin)
{
    Console.WriteLine($"{item.Id} {item.Name} {item.HouseName}");
}

输出结果是这样婶儿的:

2 李四 1号公寓
3 王五 2号公寓
4 赵六 3号公寓
5 孙七 4号公寓

然后是重头戏,outer join了。outer join也就是left join(right join在linq里就是左右集合反过来,没有别的办法实现),实现的难点在于让右侧不存在的项,放置一个空即null。

这时候聪明的你开始思考了,为空不就是DefaultIfEmpty()吗,说着随手写出了如下代码:

var innerjoin = from people in service.People
                join house in service.Houses.DefaultIfEmpty()
                on people.HouseId equals house.Id
                select new { people.Id, people.Name, HouseName = house.Name };

结果如何呢?

2 李四 1号公寓
3 王五 2号公寓
4 赵六 3号公寓
5 孙七 4号公寓

???

为什么会这样?

仔细分析就会发现,DefaultIfEmpty()这个方法作用在了service.Houses上,也就是说,只有在我们的service提供的Houses集合为空时,结果才是null,这并不符合我们上面的思路。按照我们的思路,在join右侧的集合的查询结果上调用DefaultIfEmpty()才能生效。

var innerjoin = from people in service.People
                join house in service.Houses
                on people.HouseId equals house.Id
                into tmps
                from tmp in tmps.DefaultIfEmpty()
                select new { people.Id, people.Name, HouseName = tmp.Name };

结果如下:

1 张三
2 李四 1号公寓
3 王五 2号公寓
4 赵六 3号公寓
5 孙七 4号公寓
6 周八
7 吴九
8 郑十

与上面相比,多了个into。那么这个into是什么意思呢?百度百科可以查到: into 关键字表示 将前一个查询的结果视为后续查询的生成器。

光看这么个玩意儿估计也搞不明白他到底什么意思,接下来我提一个方法你就懂上面那一坨linq关键字都是什么意思了(说实在的,linq关键字真的很反直觉)。

我们把这个linq查询语句改成等价的lambda形式,如下:

var innerjoin = service.People.GroupJoin(inner: service.Houses,
                                         outerKeySelector: people => people.HouseId,
                                         innerKeySelector: house => house.Id,
                                         resultSelector: (people, houses) => new { people.Id, people.Name, Houses = houses.DefaultIfEmpty() })
                              .SelectMany(collectionSelector: (c => c.Houses),
                                          resultSelector: (c, house) => new { c.Id, c.Name, HouseName = house == null ? "" : house.Name });

是不是一目了然了。join into语句合在一起,相当于GroupJoin方法,将右表整成一个集合,是双层的,形成一个一对多关系;而单纯的join则是平面的,一对一的。

在houses调用DefaultIfEmpty()方法,可以在houses集合为空时,向里面添加一条值为null的项,这样一来就不会被下一步的SelectMany方法忽略。


以上所述就是小编给大家介绍的《C# Linq中的outer join》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Java 语言导学

Java 语言导学

Mary Campione Kalrath Alison Huml / 机械工业 / 2003-1 / 39.00元

《Java 语言导学(原书第3版)》既适合初学者,也适合有经验的程序员:新程序员通过从头到尾阅读《Java 语言导学(原书第3版)》可以得到最大的收获,包括按照第1章“起步”中的步骤说明编译和运行自己的第一个程序。有过程式语言(比如C)经验的程序员可能希望从Java编程语言的面向对象概念和特性开始学习。 有面向对象编程经验的程序员可能希望先学习更高级的内容。一起来看看 《Java 语言导学》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

随机密码生成器
随机密码生成器

多种字符组合密码