Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> C#

Sử dụng truy vấn "NOT IN" với LINQ trong C#

LINQ không có từ khóa NOT IN trực tiếp, nhưng đạt được cùng kết quả bằng Except, Where !Contains, hoặc Where !Any.

Cách 1: Except (Set Operation - Khuyến nghị cho collection)


var listA = Enumerable.Range(1, 6);      // 1,2,3,4,5,6
var listB = new List { 3, 4 };      // 3,4

var result = listA.Except(listB);        // 1,2,5,6

foreach (var item in result) Console.WriteLine(item);
  • Loại bỏ duplicate trong listA.
  • Sử dụng default equality comparer.
  • Hiệu suất tốt (HashSet internal).

Cách 2: Where + !Contains (Linq to Objects)


var result = listA.Where(x => !listB.Contains(x)); // 1,2,5,6

// Tối ưu: Chuyển listB thành HashSet trước
var setB = new HashSet(listB);
var result = listA.Where(x => !setB.Contains(x));

Cách 3: Where + !Any (Linq to SQL / Entity Framework)


// Dành cho IQueryable (EF Core) - dịch sang SQL NOT IN / NOT EXISTS
var result = context.Products
    .Where(p => !context.DiscontinuedProducts
        .Select(dp => dp.ProductId)
        .Contains(p.Id))
    .ToList();

// Hoặc
var result = context.Products
    .Where(p => !context.DiscontinuedProducts.Any(dp => dp.ProductId == p.Id))
    .ToList();

Cách 4: Join + DefaultIfEmpty (Left Anti Join)


var result = from a in listA
             join b in listB on a equals b into gj
             from sub in gj.DefaultIfEmpty()
             where sub == 0 // default(int) = 0
             select a;

So sánh

Phương phápDùng choDuplicateEF CoreHiệu suất
ExceptIn-memory collectionsLoại bỏKhôngO(n+m)
Where !ContainsIn-memoryGiữKhôngO(n*m) -> dùng HashSet
Where !AnyEF Core / IQueryableGiữCó (NOT EXISTS)Tùy query plan
Left Anti JoinComplex join logicGiữCóTương tự !Any

Ví dụ với Object (Custom Equality)


var listA = new[] { new { Id = 1, Name = "A" }, new { Id = 2, Name = "B" } };
var listB = new[] { new { Id = 2, Name = "B" } };

// Except cần IEqualityComparer hoặc anonymous type cùng cấu trúc
var result = listA.Except(listB); // Works for anonymous type same structure

// Where !Any - flexible
var result2 = listA.Where(a => !listB.Any(b => b.Id == a.Id));

Lưu ý EF Core

  • Contains trên subquery lớn có thể gây performance issue -> dùng Any hoặc Join.
  • Not In trong SQL có vấn đề với NULL values -> NOT EXISTS (từ !Any) an toàn hơn.