内容简介:昨天用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》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。