真正复杂的 LINQ (to SQL) 查询示例

Posted

技术标签:

【中文标题】真正复杂的 LINQ (to SQL) 查询示例【英文标题】:Really complex LINQ (to SQL) query example 【发布时间】:2009-12-08 21:48:03 【问题描述】:

我们正在考虑为 ORMBattle.NET 添加更多 LINQ 测试,但没有更多想法。那里的所有 LINQ 测试都在检查常见的 LINQ 功能:

任何测试都必须通过 LINQ to IEnumerable 对于任何测试,都必须至少有一个 ORM,它通过(实际上是否在 @ORMBattle 上列出并不重要)。

目前 LINQ 测试序列的目标是自动计算 LINQ 实现覆盖率分数。

先决条件:

Code of tests for LINQ to SQL is here。这必须让您对已经涵盖的内容有所想象。 其他工具的测试(实际上它们是由this T4 template 生成的)是here。

如果您对可以添加的内容有任何想法,请分享。我肯定会接受 任何 满足上述要求的 LINQ 查询示例,并且可能 - 一些与改进测试套件相关的好主意,可以实现(例如,如果您会建议我们手动研究翻译质量,这是行不通的,因为我们无法自动执行此操作)。

【问题讨论】:

“可以拒绝支持外来类型和方法的测试。“外来”意味着它将被大多数 ORM 供应商@ORMBattle.NET Development Google Group 认为是外来的。”然后询问有权批准测试的多数来创建测试。他们基本上可以通过将其识别为“异国情调”来拒绝任何他们喜欢的东西。 问题是“为我的项目贡献一些东西吗?”这似乎不是合适的论坛。 好的,澄清一下:如果你想使用例如Uri 类型在您的测试中,很有可能会被识别为外来类型 - 仅仅是因为在关系范围内没有类似的类型,但另一方面,很可能可以使用字符串操作来模拟它的某些方法。跨度> 另一方面,我几乎不相信像 Uri 这样的类型会在未来几年内至少有一个 ORM 或 LINQ 提供商支持。好的,我将删除条件 3 - 1 和 2 来定义范围。 > 问题是“为我的项目贡献一些东西吗?”是的,部分 - 即您必须知道非常奇特的背景才能回答它。但是这个问题与编码有关,需要专业知识,并且很可能只需几个 LOC 就可以回答。所以我不太确定,决定试试这个。 【参考方案1】:

    Expression.Invoke 用于子表达式;适用于 LINQ-to-SQL 和 LINQ-to-Objects,但不适用于 3.5SP1 中的 EF(对于 IEnumerable<T>,请先调用 .AsQueryable()):

        Expression<Func<Customer, bool>> pred1 = cust=>cust.Country=="UK";
        Expression<Func<Customer, bool>> pred2 = cust=>cust.Country=="France";
        var param = Expression.Parameter(typeof(Customer), "x");
        var final = Expression.Lambda<Func<Customer, bool>>(
            Expression.OrElse(
                Expression.Invoke(pred1, param),
                Expression.Invoke(pred2, param)
            ), param);
        using (var ctx = new DataClasses1DataContext())
        
            ctx.Log = Console.Out;
            int ukPlusFrance = ctx.Customers.Count(final);
        
    

    示例 LINQ-to-SQL 输出(EF 在火花中爆炸):

    SELECT COUNT(*) AS [value]
    FROM [dbo].[Customers] AS [t0]
    WHERE ([t0].[Country] = @p0) OR ([t0].[Country] = @p1)
    -- @p0: Input NVarChar (Size = 2; Prec = 0; Scale = 0) [UK]
    -- @p1: Input NVarChar (Size = 6; Prec = 0; Scale = 0) [France]
    

    没有往返的身份管理器短路 - 即

    var obj = ctx.Single(x=>x.Id == id);
    var obj = ctx.Where(x=>x.Id == id).Single();
    
    如果具有该身份的对象已经物化并存储在身份管理器中,

    等应该不需要进入数据库;也适用于FirstSingleOrDefaultFirstOrDefault。请参阅 LINQ-to-SQL(也可以是 here 和 here;您可以通过附加到 .Log 进行验证);示例:

    using (var ctx = new DataClasses1DataContext())
    
        ctx.Log = Console.Out;
        var first = ctx.Customers.First();
        string id = first.CustomerID;
        Console.WriteLine("Any more trips?");
        var firstDup = ctx.Customers.First(x=>x.CustomerID==id);
        Console.WriteLine(ReferenceEquals(first, firstDup)); // true
        Console.WriteLine("Prove still attached");
        int count = ctx.Customers.Count();
    
    

    日志输出仅显示两次行程;一个用于第一次获取对象,一个用于计数;它还显示了物化器返回了相同的对象引用:

    SELECT TOP (1) [t0].[CustomerID], [t0].[CompanyName], [t0].[ContactName], [t0].[
    ContactTitle], [t0].[Address], [t0].[City], [t0].[Region], [t0].[PostalCode], [t
    0].[Country], [t0].[Phone], [t0].[Fax]
    FROM [dbo].[Customers] AS [t0]
    -- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 3.5.30729.492
    6
    
    Any more trips?
    True <==== this is object reference equality, not "are there any more trips"
    Prove still attached
    SELECT COUNT(*) AS [value]
    FROM [dbo].[Customers] AS [t0]
    -- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 3.5.30729.492
    6
    

    UDF 支持;举一个同样适用于 LINQ-to-Objects 的简单示例:

    partial class MyDataContext 
         [Function(Name="NEWID", IsComposable=true)] 
         public Guid Random()   return Guid.NewGuid();
    
    

    然后通过x =&gt; ctx.Random()订购;示例:

    using (var ctx = new DataClasses1DataContext())
    
        ctx.Log = Console.Out;
        var anyAtRandom = (from cust in ctx.Customers
                           orderby ctx.Random()
                           select cust).First();
    
    

    带输出:

    SELECT TOP (1) [t0].[CustomerID], [t0].[CompanyName], [t0].[ContactName], [t0].[        ContactTitle], [t0].[Address], [t0].[City], [t0].[Region], [t0].[PostalCode], [t0].[Country], [t0].[Phone], [t0].[Fax]
    FROM [dbo].[Customers] AS [t0]
    ORDER BY NEWID()
    

    如果我感觉真的邪恶,recursive lambda;可能不值得以任何方式支持它...同样,4.0 表达式 (DLR) 节点类型。

【讨论】:

非常感谢!我们肯定会在测试中添加 1 和 3。 2 似乎很主观(例如,我们决定不花任何时间在这上面)+ 暗示 SQL 查询检测(目前测试不关注 SQL),所以我会问其他人。 4 可能不受任何人支持,因为这必须暗示〜UDF 生成。所以让我们把它留到一些离开证明之前;)我同意,4 很有趣,主要是作为一个棘手的案例。但也许我们会添加一个测试以成功检测递归 lambda。 P.S. Marc,你是一位真正的专家 :) 我几乎没想到会在这里得到新的东西(我主要考虑的是特定的 VB.NET 方法),但案例 1 对我来说绝对是新的。我会问我们团队的人——他们可能知道这件事。虽然这看起来不太可能。

以上是关于真正复杂的 LINQ (to SQL) 查询示例的主要内容,如果未能解决你的问题,请参考以下文章

具有复杂过滤 LINQ to XML c# 的查询

SQL to LINQ 工具 [关闭]

LINQ TO SQL 和 ADO.NET ENTITY 有啥区别呢?

使用 linq to sql 后端通过 WCF 查询 DTO 对象

LINQ to SQL 实现 GROUP BY聚合ORDER BY

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