Linq to SQL left outer join using Lambda syntax and joining on 2 columns (composite join key)
我正在尝试使用Linq to SQL作为Lambda表达式在2列上进行内部联接。 普通查询如下所示。
1 2 3 4
| 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) |
我成功地在左列上使用以下代码进行了联接。
1 2 3 4 5 6 7 8 9 10
| 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或不是多列上的Left外部联接。
如何在LINQ中在单个联接中的多个字段上进行联接
LINQ to SQL-具有多个联接条件的左外部联接
通过Lambda表达式使用两个以上的列进行分组
而其中大部分来自此Google搜索
我可以在可在Linq2Sql和Entity Framework中使用的复合外键对barcode, event_id上获得此LEFT OUTER JOIN,并根据此查询语法示例转换为lambda语法。
这是通过创建匿名投影来实现的,该投影用于连接条件的左侧和右侧匹配:
1 2 3 4 5
| 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
... |
笔记
这种方法依赖于赋予相同匿名类的自动等同性,即:
Because the Equals and GetHashCode methods on anonymous types are defined in terms of the Equals and GetHashCode methods of the properties, two instances of the same anonymous type are equal only if all their properties are equal.
因此,对于连接键的两个投影必须是相同的类型才能是equal,编译器需要在后台将它们视为相同的匿名类,即:
-
两个匿名投影中的连接列数必须相同
-
字段类型必须是兼容的相同类型
-
如果字段名称不同,那么您将需要为它们加上别名(我使用了JoinColx)
我在这里在GitHub上放置了一个示例应用程序。
可悲的是,表达式树中的值元组尚不支持,因此您需要在投影中坚持使用匿名类型。
-
如果我将lamda更改为您建议的值,则会收到错误The type arguments for method Queryable.GroupJoin cannot be inferred from the usage
-
是的,它们都是不可为null的,ID为int,条形码为bigint。
-
我还注意到,Linq to SQL不完全相同。它与"正常" Lnq有一些差异
-
香港专业教育学院设法使这与Linq2Sql一起工作,开箱即用。我已经将我的代码推送到了[GitHub这里](我已经将示例应用程序放在了这里GitHub-也许这可能对调试有用吗?使用的Sql在这里-我特别使用了强引用复合主键和外键来帮助说服ORM
-
感谢您对GitHub示例的详细回答。我知道了起初,我仍然不断收到错误The type arguments...。但是在对您的样品进行了一些测试之后,我发现了问题所在。那是NAME列。在我的问题中,为了便于理解,我将两个列都命名为barcode,但是在我的实际数据库中,其中一个列名为barcode_int。那就是导致问题的原因,而不是导致数据类型...
-
啊哈-好的-Join键中的两种类型必须完全相同(即两个投影中使用的相同匿名类)。我已经将此添加到了答案中-希望这对将来的ORMer有用。
-
似乎对anon类使用automagic .Equals就是为了这个目的
查询:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| var owners =
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,
}; |
Lambda:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| var owners = 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 repo,然后播放!
您可以通过使用匿名类型来做到这一点。
例:
1 2 3 4
| 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 |