How would you do a “not in” query with 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和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 |
虽然
- 在您的类型中实现
IEquatable ,或者 - 在您的类型中重写
Equals 和GetHashCode ,或者 - 传入为您的类型实现
IEqualityComparer 的类型的实例
也可以使用
对于还想在C_中使用类似于SQL的
Mshwf.NiceLinq
它有
1 | var result = list1.In(x => x.Email, list2.Select(z => z.Email)); |
你也可以这样用
1 |
为了简单起见,使用int列表的示例。
1 2 3 4 5 6 7 8 9 10 11 |
如果组为空,您不能进行外部联接,只从第一个列表中选择项目吗?类似:
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 |