Виберіть декілька записів на основі списку ідентифікаторів із linq


122

У мене є список, який містить ідентифікатори моєї UserProfileтаблиці. Як я можу вибрати все UserProfilesна основі списку ідентифікаторів, які я отримав у varкористуванні LINQ?

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

Я застряг тут. Я можу це зробити за допомогою циклів і т. Д. Але я вважаю за краще це зробити LINQ.


4
пошук і пошук - це дві різні речі. Але оскільки ви можете дивитись через моє плече через Інтернет, чи не могли б ви сказати мені, як ви знаєте, що я не шукав? чекай, не кажи! Ви це бачили правильно? моя річ точно.
Юстме

5
ставити запитання коштує більше часу, ніж пошук. наступного разу просто припустимо, що він / вона зробили обшук або 10.
Юстме

2
Це все ще привертає трохи уваги, тому я подумав, що я зазначу, що ReSharper робить дуже гарну роботу, пропонуючи місця, де ви можете перетворити ітераційний код у висловлювання LINQ. Для людей, що не знаходяться в LINQ, це може стати незамінним інструментом для досягнення цієї мети.
Юк

Відповіді:


206

Ви можете використовувати Contains()для цього. Це буде відчуватися трохи назад, коли ви дійсно намагаєтеся створити INпропозицію, але це слід зробити:

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

Я також припускаю, що кожен UserProfileзапис буде мати int Idполе. Якщо це не так, вам доведеться відповідно скоригуватися.


Привіт, так, записи користувальницьких профілів містять ідентифікатори. Так я якось би робив щось на кшталт t => t.id == idList.Contains (id)?
Юстме

Contains()буде обробляти цю перевірку рівності для кожного idзначення, якщо ви використовуєте його, як я написав у відповіді. Не потрібно чітко писати ==де-небудь, коли ви намагаєтеся порівняти елементи одного набору (масив) з іншим (таблиця бази даних).
Юк

Ну проблема полягає в тому, що t вміщує весь об'єкт UserProfile, а idList містить лише int. Компілятор скаржився на щось, але мені вдалося це виправити. Дякую.
Юстме

1
@Yuck - Не працює для мене, Каже, функція вичерпана! Вимкнено завантаження в режимі "Ледачий", але все-таки не працює.
Бхувін

1
Я отримую "Неможливо перетворити лямбда-вираз у тип" int ", оскільки це не тип делегата". Як це виправити?
Stian

90

Розв’язання з. Де і .Контенс має складність O (N квадрат). Простий .Join повинен мати набагато кращі показники (близькі до O (N) за рахунок хешування). Тож правильний код:

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

А тепер результат мого вимірювання. Я створив 100 000 UserProfiles та 100 000 ідентифікаторів. Приєднання зайняло 32 мс і. Де з. Контейнери зайняли 2 хвилини та 19 секунд! Я використовував чистий IEumerable для цього тестування, щоб довести своє твердження. Якщо ви використовуєте List замість IEnumerable, .Where і .Contains буде швидше. У будь-якому випадку різниця значна. Найшвидший. Куди .Змісти - це Set <>. Все це залежить від складності базових коламентів для .Contains. Подивіться на цю публікацію, щоб дізнатися про складність linq. Подивіться на мій тестовий зразок нижче:

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

Вихід консолі:

Час приєднання: 00: 00: 00.0322546

Пройшов. Куди. Містить час: 00: 02: 19.4072107


4
Чи можете ви зробити це резервними цифрами?
Юстме

Приємно, але мені не цікаво, якими будуть терміни при Listвикористанні. +1
Yustme

Гаразд, ось вас цікавлять таймінги: Список займав 13,1 секунди, а HashSet - 0,7 мс! Отже .Where .Contains найкраще лише у випадку HashSet (коли .Contains має складність O (1)). В інших випадках краще .Join краще
Девід Грегор

5
Я отримую Local sequence cannot be used in LINQ to SQL implementations of query operators except the Contains operator.помилку під час використання LINQ2SQL datacontext.
Mayank Raichura

3
@Yustme - продуктивність завжди є важливою . (Ненавиджу бути хлопцем "це має бути прийнята відповідь", але ...)
jleach

19

Хороші відповіді abowe, але не варто забувати одне ВАЖНАЯ річ - вони дають різні результати!

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

Це поверне 2 ряди з БД (і це може бути правильним, якщо ви просто хочете окремо відсортований список користувачів)

АЛЕ у багатьох випадках, ви можете захотіти несортованого списку результатів. Ви завжди повинні думати про це, як про запит SQL. Дивіться приклад із кошиком для покупок, щоб проілюструвати, що відбувається:

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

Це поверне 5 результатів з БД. Використання "містить" було б неправильним у цьому випадку.


13

Це повинно бути простим. Спробуйте це:

var idList = new int[1, 2, 3, 4, 5];
var userProfiles = _dataContext.UserProfile.Where(e => idList.Contains(e));
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.