将SQL查询转换为Linq Lambda

oxiaedzo  于 2022-12-06  发布在  其他
关注(0)|答案(1)|浏览(208)

我不是很精通Lambda,所以在用Lambda编写SQL查询时面临一些挑战。2我用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中写这个。提前谢谢。

carvr3hs

carvr3hs1#

对于此示例:

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是同一个表(Table 1)的别名,那么这个示例就没有任何意义,而且阅读整个内容看起来就像是试图构建一个简单的真实查询示例,但最终却编写了完全无效的内容。
您的查询基本上可以写成:

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

Entity Framework不仅仅是一个用Linq Lambda替代SQL的工具。是的,使用EF,您可以在DbSet之间编写带有Join表达式的Linq,但99.5%的情况下您永远不需要这样做。相反,您可以配置或者让EF解决表之间的关系(前提是数据库遵循公认的命名和规范化约定),EF负责后台的必要连接。
例如,您有一个包含Customers & Orders的系统。一个Customer有许多订单。这些表中的每一个都将声明一个实体,但这些实体之间的关系也将被Map。

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中,如果我们需要特定Order Number的订单和客户详细信息,您可以编写如下内容:

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

如果忽略导航属性,Linq if 将如下所示:

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记录并立即加载其客户的导航属性。现在,当您需要订单的客户详细信息时,只需访问order. Customer。或者,如果我们需要特定的Customer及其Orders:

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

如果您忘记添加Include,并且您的DbContext设置为支持延迟加载,那么只要DbContext仍然可以访问并且没有被释放,导航属性仍然可以工作。访问没有被立即加载的导航属性可能会触发对数据库的回调,以“延迟加载”相关数据。通常应该避免这种情况,因为它可能会导致一些非常严重的性能损失。尤其是在处理实体及其相关数据的集合时。如果延迟加载被禁用,则这可能导致相关数据被保留#null,或者可能不完整/不可靠,因为EF仍将填充它当时可能跟踪的任何相关实体。
您还可以更有选择性,这样就可以使用Select之类的命令(称为“Projection”)来汇总或选择要提取的数据,而不是加载有关客户和/或订单的所有内容。

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配置了实体和底层表的相关方式,并将生成SQL,以基于我们构建的Linq表达式获取我们想要的数据。在阅读数据时,我强烈建议理解并使用投影来获取数据,因为这可以大大减少DbContext需要传输和跟踪的数据量,并避免急切加载与懒惰加载的缺陷。

相关问题