Home Articles

你会如何使用LINQ进行“不在”查询?

Asked
Viewed 1881 times
276

我有两个集合,在两个集合中都有属性 Email . 我需要获取第一个列表中的项目列表,其中 Email 在第二个列表中不存在 . 使用SQL我只会使用"not in",但我不知道LINQ中的等价物 . 怎么做的?

到目前为止,我有一个加入,像......

var matches = from item1 in list1
join item2 in list2 on item1.Email equals item2.Email
select new { Email = list1.Email };

但我不能加入,因为我需要差异,加入会失败 . 我需要一些使用Contains或Exists的方法我相信 . 我还没有找到一个例子来做到这一点 .

15 Answers

  • 304

    我没有用LINQ to Entities测试这个:

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

    或者:

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

    第一个列表中第二个列表中不存在电子邮件的项目 .

    from item1 in List1
    where !(list2.Any(item2 => item2.Email == item1.Email))
    select item1;
    
  • 57

    你不能做一个外连接,只有当组是空的时才从第一个列表中选择项目吗?就像是:

    Dim result = (From a In list1
                  Group Join b In list2 
                      On a.Value Equals b.Value 
                      Into grp = Group
                  Where Not grp.Any
                  Select a)
    

    我不确定这是否会以任何有效的方式与Entity框架一起工作 .

  • 54

    我不知道这对你有帮助,但是......

    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 );
    

    来自The NOT IN clause in LINQ to SQL by Marco Russo

  • 13

    虽然 Except 是答案的一部分,但并不是完整的答案 . 默认情况下, Except (与几个LINQ运算符一样)对引用类型进行引用比较 . 要按对象中的值进行比较,您必须这样做

    • 在您的类型中实现 IEquatable<T> ,或

    • 在您的类型中覆盖 EqualsGetHashCode ,或

    • 传入为您的类型实现 IEqualityComparer<T> 的类型的实例

  • 8

    为简单起见,使用List of int的示例 .

    List<int> list1 = new List<int>();
    // fill data
    List<int> list2 = new List<int>();
    // fill data
    
    var results = from i in list1
                  where !list2.Contains(i)
                  select i;
    
    foreach (var result in results)
        Console.WriteLine(result.ToString());
    
  • 7

    对于那些也想在C#中使用类似SQL的 IN 运算符的人,请下载此包:

    Mshwf.NiceLinq

    它有 InNotIn 方法:

    var result = list1.In(x => x.Email, list2.Select(z => z.Email));
    

    即使你可以这样使用它

    var result = list1.In(x => x.Email, "a@b.com", "b@c.com", "c@d.com");
    
  • 3

    你想要Except运算符 .

    var answer = list1.Except(list2);
    

    这里有更好的解释:http://blogs.msdn.com/charlie/archive/2008/07/12/the-linq-set-operators.aspx

    NOTE: 此技术仅适用于基本类型,因为您必须实现IEqualityComparer以使用具有复杂类型的Except方法 .

  • 2

    在使用ADO.NET Entity Framework的情况下,EchoStorm的解决方案也可以完美运行 . 但我花了几分钟时间绕过它 . 假设您有一个数据库上下文dc,并希望在表x中找不到表y中未链接的行,则完整的答案答案如下所示:

    var linked =
      from x in dc.X
      from y in dc.Y
      where x.MyProperty == y.MyProperty
      select x;
    var notLinked =
      dc.X.Except(linked);
    

    回答Andy的评论,是的,在LINQ查询中可以有两个来自 . 这是一个完整的工作示例,使用列表 . 每个类Foo和Bar都有一个Id . Foo有一个“外键”引用Bar via Foo.BarId . 程序选择未链接到相应Bar的所有Foo .

    class Program
    {
        static void Main(string[] args)
        {
            // Creates some foos
            List<Foo> fooList = new List<Foo>();
            fooList.Add(new Foo { Id = 1, BarId = 11 });
            fooList.Add(new Foo { Id = 2, BarId = 12 });
            fooList.Add(new Foo { Id = 3, BarId = 13 });
            fooList.Add(new Foo { Id = 4, BarId = 14 });
            fooList.Add(new Foo { Id = 5, BarId = -1 });
            fooList.Add(new Foo { Id = 6, BarId = -1 });
            fooList.Add(new Foo { Id = 7, BarId = -1 });
    
            // Create some bars
            List<Bar> barList = new List<Bar>();
            barList.Add(new Bar { Id = 11 });
            barList.Add(new Bar { Id = 12 });
            barList.Add(new Bar { Id = 13 });
            barList.Add(new Bar { Id = 14 });
            barList.Add(new Bar { Id = 15 });
            barList.Add(new Bar { Id = 16 });
            barList.Add(new Bar { Id = 17 });
    
            var linked = from foo in fooList
                         from bar in barList
                         where foo.BarId == bar.Id
                         select foo;
            var notLinked = fooList.Except(linked);
            foreach (Foo item in notLinked)
            {
                Console.WriteLine(
                    String.Format(
                    "Foo.Id: {0} | Bar.Id: {1}",
                    item.Id, item.BarId));
            }
            Console.WriteLine("Any key to continue...");
            Console.ReadKey();
        }
    }
    
    class Foo
    {
        public int Id { get; set; }
        public int BarId { get; set; }
    }
    
    class Bar
    {
        public int Id { get; set; }
    }
    
  • 1

    对于以一组内存中对象开头并且正在查询数据库的人,我发现这是最好的方法:

    var itemIds = inMemoryList.Select(x => x.Id).ToArray();
    var otherObjects = context.ItemList.Where(x => !itemIds.Contains(x.Id));
    

    这在SQL中产生了一个很好的 WHERE ... IN (...) 子句 .

  • 1

    您可以将这两个集合放在两个不同的列表中,例如list1和list2 .

    然后就写

    list1.RemoveAll(Item => list2.Contains(Item));
    

    这会奏效 .

  • 1
    var secondEmails = (from item in list2
                        select new { Email = item.Email }
                       ).ToList();
    
    var matches = from item in list1
                  where !secondEmails.Contains(item.Email)
                  select new {Email = item.Email};
    
  • 0

    您可以使用Where和Any的组合来查找不在:

    var NotInRecord =list1.Where(p => !list2.Any(p2 => p2.Email  == p.Email));
    
  • 0

    人们也可以使用 All()

    var notInList = list1.Where(p => list2.All(p2 => p2.Email != p.Email));
    
  • 0

    谢谢你,布雷特 . 你的建议对我也有帮助 . 我有一个对象列表,并希望使用另一个对象列表来过滤它 . 再次感谢....

    如果有人需要,请查看我的代码示例:

    'First, get all the items present in the local branch database
    Dim _AllItems As List(Of LocalItem) = getAllItemsAtBranch(BranchId, RecordState.All)
    
    'Then get the Item Mappings Present for the branch
    Dim _adpt As New gItem_BranchesTableAdapter
    Dim dt As New ds_CA_HO.gItem_BranchesDataTable
        _adpt.FillBranchMappings(dt, BranchId)
    
    Dim _MappedItems As List(Of LocalItem) = (From _item As LocalItem In _AllItems Join _
        dr As ds_CA_HO.gItem_BranchesRow In dt _
        On _item.Id Equals dr.numItemID _
        Select _item).ToList
    
    _AllItems = _AllItems.Except(_MappedItems.AsEnumerable).ToList
    
     Return _AllItems
    

Related