【问题标题】:SQL query to Lambda Expression C#对 Lambda 表达式 C# 的 SQL 查询
【发布时间】:2020-07-06 04:45:31
【问题描述】:

SQL 查询:

SELECT M.MailItemId, m.Subject, ISNULL(m.SendCC, ''), ISNULL(attachments.counter, 0) Counters, M.CreationDate
FROM MailItem AS M
LEFT OUTER JOIN (
    SELECT MailItemId, COUNT(MailItemId) counter
    FROM Attachment Group By MailItemId
) AS attachments ON M.MailItemId = attachments.MailItemId

有两个模型 MailItem 和 Attachment。 MailItemId 是 MailItem 中的主键和 Attachment 模型中的外键。 想要将上述查询转换为 lambda 表达式。

【问题讨论】:

  • 请不要撤消编辑...不格式化就无法复制。
  • 到目前为止,您对 Linq 进行了哪些尝试?向我们展示您的代码和实体模型等。

标签: sql-server entity-framework linq lambda


【解决方案1】:

所以你有一个MailItems 的表和一个Attachments 的表。在MailItemsAttachments 之间存在直接的一对多关系:每个MailItem 都有零个或多个Attachments,并且每个Attachment 恰好属于一个MailItem,即MailItem外键引用。

很多时候,人们想要“带有附件的邮件项”,他们不想要左外连接:

  • 带有附件 A、C 的邮件项 1
  • 带有附件 B、D、F 的 MailItem 2
  • 没有附件的邮件项 3
  • 带有附件 E 的邮件项 4

另一方面,您似乎更喜欢以下结果:

MailItem Attachment
    1        A
    2        B
    1        C
    3      <null>
    4        E
    2        D
    2        F

很少需要这种解决方案。这就是为什么没有标准的 LINQ 方法。

要获取带有附件的 MailItem,请使用 GroupJoin。如果您想要左外连接,请在 GroupJoin 之后使用 SelectMany。

带有附件的邮件项

var mailItemsWithTheirAttacments = dbContext.MailItems.GroupJoin(
dbContext.Attachments,                         // GroupJoin MailItems with Attachments

mailitem => mailItem.MailItemId,               // from every MailItem take the primary key
attachment => attachment.MailItemId,           // from every Attachment take the foreign key

// parameter resultSelector: for every MainItem and all its Attachments
// make one new object
(mailItem, attachmentsOfThisMailItem) => new
{
    // Select the MailItem properties that you plan to use
    Id = mailItem.Id,
    Sender = mailItem.Sender,
    ...

    Attachments = attachmentsOfThisMailItem.Select(attachment => new
    {
         // Select only the Attachments properties that you plan to use:
         Id = attachment.Id,
         Format = attachment.Format,
         ...

         // not needed, you already know the value:
         // MailItemId = attachment.MailItemId,
    })
    .ToList(),
})

左外连接

var result = dbContext.MailItems.GroupJoin(dbContext.Attachments,
    mailitem => mailItem.MailItemId,
    attachment => attachment.MailItemId,
    (mailItem, attachmentsOfThisMailItem) => new
    {
         MailItem = mailItem,
         Attachments = attachmentsOfThisMailItem,
    })

    // make it a left outer join, using SelectMany
    .SelectMany(groupJoinResult => groupJoinResult.Attachments,

    (groupJoinResult, attachment) => new
    {
        MailId = groupJoinResult.MailItem.Id,
        Sender = groupJoinResult.MailItem.Sender,
        ...

        AttachmentId = attachment.Id,
        Format = attachment.Format,
        ...
    });

使用虚拟 ICollections

如果您遵循实体框架约定,您将拥有类似于以下的类:

class MailItem
{
    public int Id {get; set; }
    ...

    // every MailItem has zero or more Attachments (one-to-many)
    public virtual ICollection<Attachment> Attachments {get; set;}
}

class Attachment
{
    public int Id {get; set; }
    ...

    // every Attachment belongs to exactly one MailItem, using foreign key:
    public int MailItemId {get; set;}
    public virtual MailItem MailItem {get; set;}
}

您可以使用其他标识符,但重要的部分是虚拟属性。

在实体框架中,表的列由非虚拟属性表示。虚拟属性表示表之间的关系(一对多,多对多,...)

外键是附件表中的一列。因此属性 Attachment.MailItemId 不是虚拟的。属性 Attachment.MailItem 表示关系,因此它是虚拟的。

如果您已将关系定义为属性,则可以使用它们为您执行 GroupJoin 或 Left Outer Join:

var mailItemsWithTheirAttachments = dbContext.MailItems.Select(mailItem => new
{
    Id = mailItem.Id,
    Sender = mailItem.Sender,
    ...

    Attachments = mailItem.Attachments
        .Where(attachment => ....)     // only if you don't want all Attachments
        .Select(attachment => new
        {
            Id = attachment.Id,
            ...
        })
        .ToList(),
});

实体框架知道关系,并会为你做正确的(组)加入。

恕我直言,这看起来比 GroupJoin 更自然,无论后面是否有 selectMany

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多