Выберите несколько записей на основе списка Id с linq

У меня есть список, содержащий Id моей таблицы UserProfile. Как я могу выбрать все UserProfiles на основе списка Id i, полученного в var, используя LINQ?

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

Я застрял прямо здесь. Я могу сделать это, используя для циклов и т.д. Но я предпочел бы сделать это с помощью LINQ.

Ответ 1

Вы можете использовать Contains(). Если вы действительно пытаетесь создать предложение IN, это будет немного немного назад, но это должно сделать это:

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

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

Ответ 2

Решение с .Where и .Contains имеет сложность O (N square). Простой .Join должен иметь намного лучшую производительность (близко к O (N) из-за хэширования). Поэтому правильный код:

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

И теперь результат моих измерений. Я создал 100 000 UserProfiles и 100 000 идентификаторов. Присоединение заняло 32 мс и. Где с .Contains заняло 2 минуты и 19 секунд! Я использовал чистый IEnumerable для этого тестирования, чтобы доказать мое утверждение. Если вы используете List вместо IEnumerable,.Where и .Contains будут быстрее. В любом случае разница значительна. Самый быстрый. Где. Состоит из Set < > . Все это зависит от сложности базовых колесий для. Посмотрите этот пост, чтобы узнать о сложности 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

Ответ 3

Это должно быть просто. Попробуйте следующее:

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

Ответ 4

Хорошие ответы отвечают, но не забудьте одну ВАЖНУЮ вещь - они дают разные результаты!

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

Это приведет к возврату двух строк из БД (и это может быть правильным, если вы хотите только отдельный отсортированный список пользователей)

НО, во многих случаях вам может понадобиться не отсортированный список od. Вы всегда должны думать об этом, как о запросе 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 результатов из БД. Использование 'contains' было бы неправильным в этом случае.