关于c#:你如何使用LINQ进行“不在”查询?

How would you do a “not in” query with LINQ?

我有两个集合,在两个集合中都有Email属性。我需要得到第一个列表中第二个列表中不存在Email的项目的列表。使用SQL,我只会使用"not in",但我不知道linq中的等效项。怎么做到的?

到目前为止,我加入了,就像…

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

但我不能加入,因为我需要不同,加入会失败。我需要某种方式使用包含或存在我相信。我只是还没有找到一个这样做的例子。


你要例外的接线员。

1
var answer = list1.Except(list2);

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

注意:此技术仅适用于基元类型,因为您必须实现IEqualityComparer才能将except方法用于复杂类型。


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

1
2
3
4
5
6
7
8
9
10
11
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的NOT IN子句到Marco Russo的SQL


items in the first list where the Email does not exist in the second list.

1
2
3
from item1 in List1
where !(list2.Any(item2 => item2.Email == item1.Email))
select item1;

对于以一组内存中的对象开始并查询数据库的用户,我发现这是最好的方法:

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

这在SQL中产生了一个不错的WHERE ... IN (...)子句。


您可以使用Where和Any的组合来查找Not In:

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

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

然后就写

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

这行得通。


在使用ADO.NET实体框架的情况下,Echostorm的解决方案也非常有效。但我花了几分钟才把头绕起来。假设您有一个数据库上下文dc,并且希望查找表x中未链接到表y中的行,那么完整的答案如下:

1
2
3
4
5
6
7
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);

对于安迪的评论,是的,一个LINQ查询中可以有两个FROM。下面是一个完整的工作示例,使用列表。每个类(foo和bar)都有一个ID。foo通过foo.bar id对bar有一个"外键"引用。程序选择所有未链接到相应栏的foo。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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
2
3
4
5
6
7
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};

虽然Except是答案的一部分,但它不是完整的答案。默认情况下,Except(与几个linq操作符一样)对引用类型进行引用比较。要按对象中的值进行比较,必须

  • 在您的类型中实现IEquatable,或者
  • 在您的类型中重写EqualsGetHashCode,或者
  • 传入为您的类型实现IEqualityComparer的类型的实例


也可以使用All()

16


对于还想在C_中使用类似于SQL的IN运算符的任何人,请下载此包:

Mshwf.NiceLinq

它有INNotIn两种方法:

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

你也可以这样用

1
var result = list1.In(x => x.Email,"[email protected]","[email protected]","[email protected]");


为了简单起见,使用int列表的示例。

1
2
3
4
5
6
7
8
9
10
11
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());

如果组为空,您不能进行外部联接,只从第一个列表中选择项目吗?类似:

1
2
3
4
5
6
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)

我不确定这是否能在实体框架中以任何有效的方式工作。


我没有与Linq to实体一起测试:

1
2
3
4
5
6
7
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;

可选地:

1
2
3
4
5
6
7
8
9
10
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 );

谢谢你,布雷特。你的建议对我也有帮助。我有一个对象列表,想用另一个对象列表来过滤它。再次感谢……

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
'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