我正在尝试使用 Linq to SQL 作为 Lambda 表达式在 2 列上制作内部连接。正常查询如下所示。
SELECT * FROM participants
LEFT OUTER JOIN prereg_participants ON prereg_participants.barcode = participants.barcode
AND participants.event_id = prereg_participants.event_id
WHERE (participants.event_id = 123)
我成功地使用以下代码在一列上制作了左外部连接。
var dnrs = context.participants.GroupJoin(
context.prereg_participants,
x => x.barcode,
y => y.barcode,
(x, y) => new { deelnr = x, vi = y })
.SelectMany(
x => x.vi.DefaultIfEmpty(),
(x, y) => new { deelnr = x, vi = y })
.Where(x => x.deelnr.deelnr.event_id == 123)
.ToList();
问题是,使用上述 Lambda,我得到了太多的结果,因为它缺少AND participants.event_id = prereg_participants.event_id
部分。但无论我尝试什么,我都没有获得正确数量的参与者。
我查看了以下现有问题,但没有一个在编写正确的 lambda 时解决了我的问题。大多数解决方案都是 lambda 格式的 nog 或不是多列上的左外部连接。
如何在 LINQ 中对单个联接中的多个字段进行联接
LINQ to SQL - 具有多个联接条件的左外联接
按 Lambda 表达式使用两列以上的列进行分组
其中大部分来自这个谷歌搜索
查询:
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,
});
查看代码,或克隆我的 git 存储库,然后玩!
我能够在复合外键对上获得此LEFT OUTER JOIN
,barcode, event_id
在 Linq2Sql 和实体框架中工作,并根据此查询语法示例转换为 lambda 语法。
这通过创建一个匿名投影来工作,该投影用于匹配连接条件的左侧和右侧:
var dnrs = context.participants.GroupJoin(
context.prereg_participants,
x => new { JoinCol1 = x.barcode, JoinCol2 = x.event_id }, // Left table join key
y => new { JoinCol1 = y.barcode, JoinCol2 = y.event_id }, // Right table join key
...
笔记
这种方法依赖于赋予相同匿名类的自动等式,即:
由于匿名类型的 Equals 和 GetHashCode方法是根据属性的 Equals 和 GetHashCode 方法定义的,因此仅当同一匿名类型的两个实例的所有属性都相等时,它们才相等。
因此,对于连接键的两个投影需要具有相同的类型才能equal
,编译器需要将它们视为幕后的同一匿名类,即:
- 两个匿名投影中的联接列数必须相同
- 字段类型必须与类型相同且可兼容
- 如果字段名称不同,则需要为它们设置别名(我用过
JoinColx
(
我在GitHub上放了一个示例应用程序。
遗憾的是,表达式树中尚不支持值元组,因此您需要在投影中坚持使用匿名类型。
如果是左外部连接,其中左实体与右实体可以有零个或最多一个连接,则可以使用:
// Let's have enumerables "left" and "right"
// and we want to join both full entities with nulls if there's none on the right.
left.GroupJoin(
right,
l => l.LeftKey,
r => r.RightKey,
(l, r) => new { Left = l, Right = r.FirstOrDefault() });
如果要仅使用右边的一个属性加入左边:
// Let's have enumerables "left" and "right"
// and we want to join right's attribute RightId and to set 0 for those having no Id.
left.GroupJoin(
right,
l => l.LeftKey,
r => r.RightKey,
(l, r) => new { Left = l, RightId = r.FirstOrDefault()?.RightId ?? 0 });
您可以通过使用匿名类型来执行此操作。
例:
var result = from a in context.participants
join b context.prereg_participants on new { X = a.barcode, Y = a.event_id } equals new { X = b.barcode, Y = b.event_id } into A
from b in A.DefaultIfEmpty()
where a.event_id = 123