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

yws3nbqq  于 2022-10-22  发布在  其他
关注(0)|答案(4)|浏览(139)

我正在尝试用LinqtoSQL作为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,或者不是多列的Left外部连接。
How to do joins in LINQ on multiple fields in single join
LINQ to SQL - Left Outer Join with multiple join conditions
i1 j2 k1 l
其中大部分来自this Google search

rryofs0p

rryofs0p1#

查询:

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,然后玩!

tvz2xvvm

tvz2xvvm2#

我能够在复合外键对barcode, event_id上获得这个LEFT OUTER JOIN,它可以在Linq2Sql和Entity Framework中使用,并根据这个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方法是根据属性的equal和GetHash Code方法定义的,所以只有当所有属性相等时,同一匿名类型的两个示例才相等。
因此,对于连接键的两个投影需要具有相同的类型才能成为equal,编译器需要将它们视为幕后的相同匿名类,即:

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

我在GitHub here上安装了一个示例应用程序。
遗憾的是,目前还没有对value tuples in expression trees的支持,因此您需要在投影中坚持匿名类型。

63lcw9qa

63lcw9qa3#

如果是LEFT OUTER JOIN,其中左实体可以与右实体有零个或最多一个连接,则可以使用:

// 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() });

如果只想用right的一个属性连接left:

// 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 });
nukf8bse

nukf8bse4#

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

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

相关问题