Linq to SQL 使用 Lambda 语法进行左外连接并在 2 列上连接(复合连接键)

Posted

技术标签:

【中文标题】Linq to SQL 使用 Lambda 语法进行左外连接并在 2 列上连接(复合连接键)【英文标题】:Linq to SQL left outer join using Lambda syntax and joining on 2 columns (composite join key) 【发布时间】:2018-10-31 23:35:11 【问题描述】:

我正在尝试使用 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,或者不是多列上的左外连接。

How to do joins in LINQ on multiple fields in single join

LINQ to SQL - Left Outer Join with multiple join conditions

Group By using more than two columns by Lambda expression

其中大部分来自this Google search

【问题讨论】:

【参考方案1】:

查询:

        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,
            );

查看code,或克隆my git repo,然后玩!

【讨论】:

【参考方案2】:

我能够在组合外键对 barcode, event_id 上获得此 LEFT OUTER JOIN,并在 Linq2Sql 和实体框架中工作,并根据此 query syntax example 转换为 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
    ...

备注

这种方法依赖于给相同匿名类的automagic equality,即:

因为匿名类型的 Equals 和 GetHashCode 方法是根据属性的 Equals 和 GetHashCode 方法定义的,所以相同匿名类型的两个实例只有在它们的所有属性都相等时才相等。

所以对于连接键的两个投影需要是相同类型才能成为equal,编译器需要在幕后将它们视为同一个匿名类,即:

两个匿名投影中连接的列数必须相同 字段类型必须是相同类型的兼容 如果字段名称不同,则需要给它们起别名(我使用过JoinColx

我已经在GitHub here 上放了一个示例应用程序。

遗憾的是,尚不支持 value tuples in expression trees,因此您需要在投影中坚持使用匿名类型。

【讨论】:

如果我将 lamda 更改为您建议的内容,我会收到错误 The type arguments for method Queryable.GroupJoin cannot be inferred from the usage 是的,它们都是不可为空的,int 用于 ID,bigint 用于条形码。 我还注意到 Linq to SQL 并不完全相同。它与“正常” Lnq 有一些不同 感谢您对 GitHub 示例的详细回答。我让它工作了。起初我仍然不断收到错误The type arguments...。但是在对您的样品进行一些测试后,我发现了问题。它是列 NAME??。在我的问题中,为了便于理解,我将两列都命名为barcode,但在我的真实数据库中,其中一列名为barcode_int。这就是导致问题的原因,而不是 DataType... 啊哈——好点——Join 键中的两种类型必须是完全相同的类型(即在两个投影中使用相同的匿名类)。我已将此添加到答案中 - 希望这对未来的 ORMer 有用。【参考方案3】:

您可以通过使用匿名类型来做到这一点。

例子:

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

【讨论】:

这不是 lambda 语法

以上是关于Linq to SQL 使用 Lambda 语法进行左外连接并在 2 列上连接(复合连接键)的主要内容,如果未能解决你的问题,请参考以下文章

LINQ TO SQL

SQL-LINQ-Lambda语法对照

如何通过 LINQ to Sql 结果上的数据对分组进行 lambda?

在 C# 中使用 LINQ 方法语法 (LINQ to SQL) 时发生异常

如何使用方法语法在 linq to sql 中进行连接?

如何使用 Entity Framework Core 2.0 上的 lambda 语法在 LINQ 中实现 LEFT OUTER JOIN?