繁体   English   中英

将 SQL 查询转换为 Linq Lambda

[英]Convert SQL query To Linq Lambda

我不是很精通 Lambda,因此在 Lambda 中编写 SQL 查询时面临一些挑战。我已经通过几种方式在 SQL 中编写了该查询:

SELECT A.*
FROM Table1 A
WHERE A.Col1 IN (
    SELECT B.Col1
    FROM Table1 B
    JOIN Table2 C ON B.Col1 = C.Col1
    WHERE B.Col2 = 'Condition'
    )

要么

SELECT A.*
FROM Table1 A
,Table1 B
,Table2 C
WHERE A.Col1 = B.Col1
AND B.Col1 = C.Col1
AND B.Col2 = 'Condition'

请有人帮助我使用 lambda 在 Linq 中写这篇文章。提前致谢。

对于这个例子:

SELECT A.*
FROM Table1 A
WHERE A.Col1 IN (
    SELECT B.Col1
    FROM Table1 B
    JOIN Table2 C ON B.Col1 = C.Col1
    WHERE B.Col2 = 'Condition'
    )

该示例没有任何意义,因为 A 和 B 是同一个表 (Table1) 的别名,并且阅读整个内容看起来您已经尝试构建一个您面临的真实世界查询的简单示例,但最终以写一些完全无效的东西。

您的查询基本上可以写成:

SELECT A.*
FROM Table1 A
INNER JOIN Table2 C ON A.Col1 = C.Col1;

实体框架不仅仅是将 SQL 替换为 Linq Lambda 的工具。 是的,使用 EF,您可以在 DbSet 之间使用Join表达式编写 Linq,但 99.5% 的时间您永远不需要这样做。 相反,您配置或让 EF 计算出表之间的关系(前提是数据库遵循公认的命名和规范化约定),EF 负责在幕后进行必要的连接。

例如,您有一个包含客户和订单的系统。 一个客户有很多订单。 这些表中的每一个都声明了一个实体,但这些实体之间的关系也将被映射。

public class Customer
{
    [Key]
    public int CustomerId { get; set; }
    
    // Other Customer details...

    public virtual ICollection<Order> Orders { get; set; } = new List<Order>();
}

public class Order
{
    [Key]
    public int OrderId { get; set; }
    public string OrderNumber { get; set; }

    // Other Order details...

    [ForeignKey(nameof(Customer))]
    public int CustomerId { get; set; }
    public virtual Customer Customer { get; set; }
}

在某些示例中,您可能会发现缺少[ForeignKey]或关系配置等内容。 EF 可以按照约定自动计算出关系,不幸的是,这些关系可能有大约 3 或 4 种不同的配置方式。 (注释,与 EntityTypeConfiguration 中或 OnModelCreating 期间或通过约定的显式配置对比)因此,不幸的是,示例可能会有些混乱,具体取决于您查看的位置。 我通常建议始终使用显式配置以避免意外。

现在在 SQL 中,如果我们想要特定订单号的订单和客户详细信息,您可以这样写:

SELECT *
FROM Orders o
INNER JOIN Customers c ON o.CustomerId = c.CustomerId
WHERE o.OrderNumber = '%OrderNumber%';

如果我们忽略导航属性, Linq 将类似于:

var details = context.Orders
    .Where(o => o.OrderNumber == orderNumber)
    .Join(context.Customers, o => o.CustomerId, c => c.CustomerId, 
        (o, c) => new {Order = o, Customer = c})
    .Single();

老实说,这很不雅观。 但是,对于不使用Join的导航属性,您只需访问或急切加载关联的导航属性。 EF 计算出幕后所需的 SQL 个 JOIN:

var order = context.Orders
    .Include(o => o.Customer)
    .Single(o => o.OrderNumber == orderNumber);

在这里,我们加载单个订单记录并为其客户预先加载导航属性。 现在,当您需要订单的客户详细信息时,只需访问 order.Customer。 或者,如果我们想要一个特定的客户及其订单:

var customer = context.Customers
    .Include(c => c.Orders)
    .Single(c => c.CustomerId == customerId);

如果您忘记添加Include并且您的 DbContext 设置为支持延迟加载,那么导航属性仍然可以 function 只要 DbContext 仍然可访问且未被释放。 访问未预加载的导航属性可以触发对数据库的回调以“延迟加载”相关数据。 这通常应该避免,因为它会导致一些相当陡峭的性能损失,尤其是在处理 collections 个实体及其相关数据时。 如果延迟加载被禁用,那么这可能导致相关数据被保留为#null,或者可能不完整/不可靠,因为 EF 仍会填充它当时可能正在跟踪的任何相关实体。

您也可以更有选择性,而不是加载有关客户和/或订单的所有内容,您可以使用Select类的命令总结或选择性地提取数据。 (称为“投影”)

var customers = context.Customers
    .Where(c => c.Orders.Any(o => o.OrderDate >= startDate))
    .Select(c => new CustomerSummaryDto
    {
        CustomerId = c.CustomerId,
        OrderCount = c.Orders.Count(o => o.OrderDate >= startDate),
        RecentOrders = c.Orders
            .Where(o => o.OrderDate >= startDate)
            .OrderByDescending(o => o.OrderDate)
            .Select(o => new OrderSummaryDto
            {
                 OrderId = o.OrderId,
                 OrderDate = o.OrderDate,
                 Value = o.Value
            }).Take(5).ToList()
    }).ToList();

例如,要获得在给定日期之后有订单的客户的摘要,列出客户的一些字段、该日期的订单计数以及(最多)5 个最近订单的摘要。 请注意,根本不需要IncludeJoin语句。 EF 配置了实体和基础表的关联方式,并将根据我们构建的 Linq 表达式生成获取所需数据所需的 SQL。 在读取数据时,我强烈建议理解并使用投影来获取数据,因为这可以大大减少 DbContext 需要传输和跟踪的数据量,并避免急切加载与延迟加载的陷阱。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM