Chọn nhiều bản ghi dựa trên danh sách Id của linq


122

Tôi có một danh sách chứa Id của UserProfilebảng của tôi . Làm thế nào tôi có thể chọn tất cả UserProfilesdựa trên danh sách Id mà tôi đã varsử dụng LINQ?

var idList = new int[1, 2, 3, 4, 5];
var userProfiles = _dataContext.UserProfile.Where(......);

Tôi đã bị mắc kẹt ngay tại đây. Tôi có thể làm điều này bằng cách sử dụng vòng lặp for, v.v. Nhưng tôi muốn làm điều này với LINQ.


4
tìm kiếm và tìm kiếm là 2 việc khác nhau. Nhưng vì bạn có thể nhìn qua vai tôi thông qua internet, bạn có thể cho tôi biết làm thế nào bạn biết tôi đã không tìm kiếm? chờ đợi không nói! Bạn đã thấy nó phải không? quan điểm của tôi chính xác.
Yustme

5
đặt một câu hỏi tốn nhiều thời gian hơn thực hiện tìm kiếm. lần sau chỉ cần giả sử 'anh ấy / cô ấy' đã thực hiện một tìm kiếm hoặc 10.
Yustme

2
Điều này vẫn nhận được khá nhiều sự chú ý, vì vậy tôi nghĩ rằng tôi sẽ đề cập đến việc ReSharper thực hiện một công việc rất tốt trong việc đề xuất những nơi mà bạn có thể biến mã lặp thành các câu lệnh LINQ. Đối với những người mới sử dụng LINQ, nó có thể là một công cụ không thể thiếu để có riêng cho mục đích này.
Yuck

Câu trả lời:


206

Bạn có thể sử dụng Contains()cho điều đó. Nó sẽ cảm thấy hơi ngược khi bạn thực sự cố gắng tạo ra một INmệnh đề, nhưng điều này nên làm điều đó:

var userProfiles = _dataContext.UserProfile
                               .Where(t => idList.Contains(t.Id));

Tôi cũng giả định rằng mỗi UserProfilebản ghi sẽ có một int Idtrường. Nếu không, bạn sẽ phải điều chỉnh cho phù hợp.


Xin chào, có, các bản ghi userprofile chứa id. Vì vậy, bằng cách nào đó tôi sẽ làm một cái gì đó như t => t.id == idList.Contains (id)?
Yustme

Contains()sẽ xử lý kiểm tra bình đẳng đó trên mỗi idgiá trị nếu bạn sử dụng nó như tôi đã viết trong câu trả lời. Bạn không cần phải viết rõ ràng ở ==bất kỳ đâu khi bạn đang cố gắng so sánh các mục của một tập hợp (mảng) với tập hợp khác (bảng cơ sở dữ liệu).
Yuck

Vấn đề là t chứa toàn bộ đối tượng của UserProfile và idList chỉ chứa int. Trình biên dịch đã phàn nàn về điều gì đó nhưng tôi đã cố gắng sửa nó. Cảm ơn.
Yustme

1
@Yuck - Không hoạt động với tôi, nói Chức năng đã hết thời gian! Đã vô hiệu hóa Lazy loading nhưng vẫn không thành công.
bhuvin

1
Tôi nhận được "Không thể chuyển đổi biểu thức lambda thành kiểu 'int' vì nó không phải là kiểu đại biểu". Làm thế nào để khắc phục điều đó?
Stian

91

Lời giải với .Where và .Contains có độ phức tạp là O (N hình vuông). .Join đơn giản nên có hiệu suất tốt hơn rất nhiều (gần bằng O (N) do băm). Vì vậy, mã chính xác là:

_dataContext.UserProfile.Join(idList, up => up.ID, id => id, (up, id) => up);

Và bây giờ là kết quả đo của tôi. Tôi đã tạo 100 000 UserProfiles và 100 000 id. Tham gia mất 32ms và .Where with .Contains mất 2 phút và 19 giây! Tôi đã sử dụng IEnumerable thuần túy cho thử nghiệm này để chứng minh tuyên bố của mình. Nếu bạn sử dụng List thay vì IEnumerable, .Where và .Contains sẽ nhanh hơn. Dù sao sự khác biệt là đáng kể. Nhanh nhất .Where .Contains là với Set <>. Tất cả điều đó phụ thuộc vào độ phức tạp của các khái niệm cơ bản cho .Contains. Hãy xem bài đăng này để tìm hiểu về độ phức tạp của linq. Hãy xem mẫu thử nghiệm của tôi bên dưới:

    private static void Main(string[] args)
    {
        var userProfiles = GenerateUserProfiles();
        var idList = GenerateIds();
        var stopWatch = new Stopwatch();
        stopWatch.Start();
        userProfiles.Join(idList, up => up.ID, id => id, (up, id) => up).ToArray();
        Console.WriteLine("Elapsed .Join time: {0}", stopWatch.Elapsed);
        stopWatch.Restart();
        userProfiles.Where(up => idList.Contains(up.ID)).ToArray();
        Console.WriteLine("Elapsed .Where .Contains time: {0}", stopWatch.Elapsed);
        Console.ReadLine();
    }

    private static IEnumerable<int> GenerateIds()
    {
       // var result = new List<int>();
        for (int i = 100000; i > 0; i--)
        {
            yield return i;
        }
    }

    private static IEnumerable<UserProfile> GenerateUserProfiles()
    {
        for (int i = 0; i < 100000; i++)
        {
            yield return new UserProfile {ID = i};
        }
    }

Đầu ra bảng điều khiển:

Đã trôi qua. Thời gian tham gia: 00: 00: 00.0322546

Đã trôi qua .Where .Contains time: 00: 02: 19.4072107


4
Bạn có thể sao lưu điều đó bằng những con số không?
Yustme

Tốt, tuy nhiên, điều đó làm tôi tò mò về thời gian sẽ như thế nào khi Listđược sử dụng. +1
Yustme

Ok, đây là thời gian bạn quan tâm: Danh sách mất 13,1 giây và HashSet mất 0,7 mili giây! Vì vậy, .Where .Contains chỉ tốt nhất trong trường hợp HashSet (khi .Contains có độ phức tạp O (1)). Trong các trường hợp khác, .Join tốt hơn
David Gregor

5
Tôi gặp Local sequence cannot be used in LINQ to SQL implementations of query operators except the Contains operator.lỗi khi sử dụng văn bản dữ liệu LINQ2SQL.
Mayank Raichura,

3
@Yustme - hiệu suất luôn là điều cần cân nhắc. (Tôi ghét phải là "điều này sẽ là câu trả lời được chấp nhận" anh chàng, nhưng ...)
jleach

19

Câu trả lời tuyệt vời, nhưng đừng quên một điều QUAN TRỌNG - chúng cung cấp các kết quả khác nhau!

  var idList = new int[1, 2, 2, 2, 2]; // same user is selected 4 times
  var userProfiles = _dataContext.UserProfile.Where(e => idList.Contains(e)).ToList();

Điều này sẽ trả về 2 hàng từ DB (và điều này có thể đúng, nếu bạn chỉ muốn một danh sách người dùng được sắp xếp riêng biệt)

NHƯNG trong nhiều trường hợp, bạn có thể muốn có một danh sách kết quả không được sắp xếp . Bạn luôn phải nghĩ về nó giống như về một truy vấn SQL. Vui lòng xem ví dụ với giỏ hàng eshop để minh họa những gì đang xảy ra:

  var priceListIDs = new int[1, 2, 2, 2, 2]; // user has bought 4 times item ID 2
  var shoppingCart = _dataContext.ShoppingCart
                     .Join(priceListIDs, sc => sc.PriceListID, pli => pli, (sc, pli) => sc)
                     .ToList();

Điều này sẽ trả về 5 kết quả từ DB. Sử dụng 'chứa' sẽ là sai trong trường hợp này.


13

Điều đó phải đơn giản. Thử cái này:

var idList = new int[1, 2, 3, 4, 5];
var userProfiles = _dataContext.UserProfile.Where(e => idList.Contains(e));
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.