【问题标题】:LINQ Query: Determining if object in one list exists in another based on keyLINQ 查询:根据键确定一个列表中的对象是否存在于另一个列表中
【发布时间】:2011-01-30 21:18:27
【问题描述】:

基本上,我要做的是获取对象列表并根据多个条件对其进行过滤,其中一个条件是密钥不存在于另一个列表中。这是一个例子:
我的两个类是这样的:

public class Test  
{  
  public string name;  
  public string instructor_name;  
  public string course;  
}  

public class Appointment  
{  
  public string site;
  public DateTime forWhen;
  public string testName;
}

我想通过查看课程并确保 List 中不存在该测试来对 List 进行排序。在 SQL 中,我会这样做:

SELECT new Group<Test>(c.Key, c)
FROM tests in testList
WHERE tests.Course != "Science"
AND tests.name NOT IN (SELECT testName FROM appotList)

但是,我不知道如何在 LINQ 中执行此操作。有什么想法吗?

【问题讨论】:

  • 你可能想看看 SQL 的第一行...它不是 SQL :)
  • 我意识到这一点,但我并不想在 SQL 中做到这一点。如果我在 SQL 中这样做,那只是我会使用的格式。我这样写是为了让人们更好地了解我在 LINQ 中试图做什么。

标签: c# linq


【解决方案1】:

如果您正在谈论执行客户端过滤,那么使用 LINQ to Objects 非常容易。像这样的:

List<Test> tests = ...;
List<Appointment> appts = ...;

var query = tests.Except(
            tests.Join(appts, t => t.name, a => a.testName, (t, a) => t));

以下内容阅读起来稍微简单一些:

var query = tests.Where(t => !appts.Any(a => a.testName == t.name));

但第一个版本会更快,因为 Join 函数将计算匹配项的哈希表,而不是为 tests 中的每个元素对 appts 列表进行线性搜索。

【讨论】:

  • 发布的第二个答案对我来说更有意义,所以我决定使用它,但我对你的帖子很好奇。您可以在 Where 方法中创建多个选择条件吗?我希望能够比较测试的日期以查看是否可以进行测试,并检查以确保尚未预约。在使用 => 运算符时我还是个新手,所以我不确定如何列出多个条件。
  • @legacybass:是的,您可以在Where 部分中包含任意数量的条件。我发布了这个版本,因为如果您要针对内存对象列表而不是创建数据库查询,它会更简单(更快)。
  • 那么这些是用逗号还是分号分隔的?您将如何在 Join 方法中使用多个条件?那是我最有兴趣学习如何使用的。
  • @legacybass:不幸的是,Join 函数不如 where 强大。它通过创建一个简单的哈希表来工作,该哈希表通过单个值链接两行。您可以加入多个条件,但只能通过跨列的简单相等比较来完成。如果你想要一个具体的例子,最好再问一个关于如何在连接子句中执行多个条件的问题,我很乐意回答:)
  • 注意区分大小写: var query = tests.Where(t => !appts.Any(a => a.testName.ToLower() == t.name.ToLower()));
【解决方案2】:

http://introducinglinq.com/blogs/marcorusso/archive/2008/01/14/the-not-in-clause-in-linq-to-sql.aspx

考虑这个代码,它返回在 Orders 表中没有订单的所有客户。这是一个返回该值的 SQL 查询。

SELECT *
FROM [dbo].[Customers] AS [t0]
WHERE [t0].[CustomerID] NOT IN (
    SELECT [t1].[CustomerID]
    FROM [dbo].[Orders] AS [t1]
)

这不是获得所需结果的更快方法(使用 NOT EXISTS 是最喜欢的方法 - 稍后会详细介绍)。 LINQ 提供了一个 Contains 扩展方法,允许编写以下代码。

NorthwindDataContext dc = new NorthwindDataContext();
dc.Log = Console.Out;
var query =
    from c in dc.Customers
    where !(from o in dc.Orders
            select o.CustomerID)
           .Contains(c.CustomerID)
    select c;
foreach (var c in query) Console.WriteLine( c );

在 LINQ to SQL 中,查询被翻译成以下 SQL 代码:

SELECT [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]
WHERE NOT (EXISTS(
    SELECT NULL AS [EMPTY]
    FROM [dbo].[Orders] AS [t1]
    WHERE [t1].[CustomerID] = [t0].[CustomerID]
    ))

这种方法不仅在语义上是等价的,而且执行起来也更快。以下是 SET STATISTICS IO ON 的结果。第一个结果是使用 NOT IN 子句的手写查询。第二个结果是针对 LINQ to SQL 生成的查询。

【讨论】:

  • 除非我误读了这个问题,否则他似乎在问如何进行客户端过滤(即 LINQ-to-Objects),而不是如何在 SQL 中进行过滤
  • @Adam - LINQ 查询仍然应该工作,不是吗?他可以使用他的对象集合代替 dc.Customers 和 dc.Orders。
  • 这实际上是我正在寻找的信息。我一定已经搜索了 3 打教程和文档站点,但从未找到任何与此相关的内容。正是我需要的。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-26
  • 1970-01-01
  • 2021-02-11
  • 1970-01-01
相关资源
最近更新 更多