Làm thế nào bạn có thể thực hiện một câu hỏi không phải trong truy vấn của LIN với LINQ?


307

Tôi có hai bộ sưu tập có tài sản Emailtrong cả hai bộ sưu tập. Tôi cần lấy danh sách các mục trong danh sách đầu tiên Emailkhông tồn tại trong danh sách thứ hai. Với SQL tôi sẽ chỉ sử dụng "không trong", nhưng tôi không biết tương đương trong LINQ. Làm thế nào được thực hiện?

Cho đến nay tôi có một người tham gia, như ...

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

Nhưng tôi không thể tham gia vì tôi cần sự khác biệt và việc tham gia sẽ thất bại. Tôi cần một số cách sử dụng Chứa hoặc Tồn tại tôi tin. Tôi chỉ chưa tìm thấy một ví dụ để làm điều đó.


3
Xin lưu ý rằng câu trả lời của Echostorm tạo ra mã dễ đọc hơn nhiều so với của Robert
Nathan Koop

Câu trả lời:


302

Tôi không biết nếu điều này sẽ giúp bạn nhưng ..

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

từ mệnh đề NOT IN trong LINQ sang SQL của Marco Russo


Nhưng tôi sử dụng linq cho các thực thể, vì vậy tôi nhận được "chỉ các loại nguyên thủy có thể được sử dụng lỗi". Có bất kỳ công việc xung quanh...? ngoài việc lặp lại thủ công và tìm danh sách.
Novice

13
Điều này hoạt động tốt với tôi với LINQ to Entities. SQL trở thành truy vấn WHERE NOT EXISTS (truy vấn con). Có lẽ đã có một bản cập nhật giải quyết điều này?
scottheckel

2
Tôi nghĩ rằng các phiên bản mới hơn của EF có hỗ trợ. Nội dung, cộng với câu hỏi này không gắn thẻ EF (phiên bản) hoặc LinqToQuery .. vì vậy có thể cần phải giới hạn câu hỏi và câu trả lời ở đây ..
Brett Caswell

4
@Robert Rouse - Liên kết đến The Not in cluse in linq to sql không còn hoạt động. Chỉ là một fyi.
JonH

Liên kết được cung cấp dẫn đến một trang web được gắn cờ là chứa phần mềm độc hại.
mikeigs

334

Bạn muốn toán tử Ngoại trừ.

var answer = list1.Except(list2);

Giải thích rõ hơn tại đây: https://docs.microsoft.com/archive/bloss/charlie/linq-farm-more-on-set-operators

LƯU Ý: Kỹ thuật này chỉ hoạt động tốt nhất cho các loại nguyên thủy, vì bạn phải triển khai IEqualityComparer để sử dụng Exceptphương thức với các loại phức tạp.


7
Sử dụng Ngoại trừ: Nếu bạn làm việc với các danh sách loại phức tạp, thì bạn phải triển khai IEqualityComparer <MyComlplexType>, điều này làm cho nó không hay
sakito

4
Bạn không phải triển khai IEqualityComparer <T> nếu bạn chỉ muốn so sánh đẳng thức tham chiếu hoặc nếu bạn đã ghi đè T.Equals () và T.GetHashCode (). Nếu bạn không triển khai IEqualityComparer <T>, EqualityComparer <T> .Default sẽ được sử dụng.
piedar

2
@Echostorm (và những người khác đang đọc), nếu bạn thực hiện một đối tượng Chọn để ẩn danh, HashCode sẽ được xác định bởi các giá trị thuộc tính; list1.Select(item => new { Property1 = item.Property1, Property2 = item.Property2 }).Except(list2.Select( item => new { Property1 = item.Property1, Property2 = item.Property2 }));điều này đặc biệt hữu ích khi bạn xác định sự bằng nhau bằng cách chỉ đánh giá một tập hợp các giá trị của loại phức tạp.
Brett Caswell

3
Trên thực tế, ai đó đã chỉ ra bên dưới, và tôi nghĩ chính xác, rằng sẽ không cần phải thực hiện IEquatityComparor<T,T>hoặc ghi đè các phương thức so sánh đối tượng trong một LinqToSqlkịch bản; cho, truy vấn sẽ được biểu diễn dưới dạng / được biên dịch thành / được biểu thị dưới dạng SQL; do đó, các giá trị sẽ được kiểm tra, không phải tham chiếu đối tượng.
Brett Caswell

2
Sử dụng exceptTôi có thể tăng tốc truy vấn LINQ từ 8-10 giây lên nửa giây
Michael Kniskern

61

Đối với những người bắt đầu với một nhóm các đối tượng trong bộ nhớ và đang truy vấn cơ sở dữ liệu, tôi thấy đây là cách tốt nhất để đi:

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

Điều này tạo ra một WHERE ... IN (...)mệnh đề đẹp trong SQL.


1
thực tế, bạn có thể làm điều đó trong 3,5
George Silva

59

các mục trong danh sách đầu tiên mà Email không tồn tại trong danh sách thứ hai.

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

16

Bạn có thể sử dụng kết hợp Địa điểm và Bất kỳ để tìm kiếm không có trong:

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

8

Bạn có thể lấy cả hai bộ sưu tập trong hai danh sách khác nhau, giả sử list1 và list2.

Sau đó chỉ cần viết

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

Điều này sẽ làm việc.


3
Đẹp nhưng có tác dụng phụ của việc loại bỏ các yếu tố khỏi danh sách.
Tarik

7

Trong trường hợp một người đang sử dụng Khung thực thể ADO.NET , giải pháp của EchoStorm cũng hoạt động hoàn hảo. Nhưng tôi phải mất vài phút để quấn đầu quanh nó. Giả sử bạn có bối cảnh cơ sở dữ liệu, dc và muốn tìm các hàng trong bảng x không được liên kết trong bảng y, câu trả lời hoàn chỉnh sẽ giống như:

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

Đáp lại bình luận của Andy, vâng, người ta có thể có hai từ trong truy vấn LINQ. Đây là một ví dụ hoạt động hoàn chỉnh, sử dụng danh sách. Mỗi lớp, Foo và Bar, có một Id. Foo có một tham chiếu "khóa ngoại" đến Bar thông qua Foo.BarId. Chương trình chọn tất cả các Foo không được liên kết với một Thanh tương ứng.

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

làm hai từ wor trong LINQ? đó sẽ là hữu ích
Andy

Andy: Vâng, xem câu trả lời sửa đổi ở trên.
Brett

4
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};

4

Ai cũng có thể sử dụng All()

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

2

Trong khi Exceptlà một phần của câu trả lời, nó không phải là toàn bộ câu trả lời. Theo mặc định, Except(như một số toán tử LINQ) thực hiện so sánh tham chiếu về các loại tham chiếu. Để so sánh theo các giá trị trong các đối tượng, bạn sẽ phải

  • thực hiện IEquatable<T>theo kiểu của bạn, hoặc
  • ghi đè EqualsGetHashCodetrong loại của bạn, hoặc
  • vượt qua trong một thể hiện của kiểu triển khai IEqualityComparer<T>cho kiểu của bạn

2
... Nếu chúng ta đang nói về LINQ to Object. Nếu đó là LINQ to SQL, truy vấn được dịch thành các câu lệnh SQL chạy trên cơ sở dữ liệu, vì vậy điều này không áp dụng.
Lucas

1

Ví dụ sử dụng Danh sách int cho đơn giản.

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

Đối với bất kỳ ai cũng muốn sử dụng INtoán tử tương tự SQL trong C #, hãy tải xuống gói này:

Mshwf.NiceLinq

Nó có InNotInphương pháp:

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

Thậm chí bạn có thể sử dụng nó theo cách này

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

0

Cảm ơn bạn, Brett. Đề nghị của bạn đã giúp tôi quá. Tôi đã có một danh sách các Đối tượng và muốn lọc nó bằng cách sử dụng một danh sách các đối tượng khác. Cảm ơn một lần nữa ....

Nếu bất cứ ai cần, xin vui lòng xem mẫu mã của tôi:

'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

0

Tôi đã không kiểm tra điều này với 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;

Cách khá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 );

0

Bạn không thể tham gia bên ngoài, chỉ chọn các mục từ danh sách đầu tiên nếu nhóm trống? Cái gì đó như:

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)

Tôi không chắc liệu điều này có hoạt động theo bất kỳ cách hiệu quả nào với khung Thực thể hay không.


0

Ngoài ra, bạn có thể làm như thế này:

var result = list1.Where(p => list2.All(x => x.Id != p.Id));
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.