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


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

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

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

4 88

4 ответа:

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

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

Я также предполагаю, что каждый UserProfile запись будет int

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

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

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

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

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

хорошие ответы 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 результаты из БД. Использование 'contains' было бы неправильным в этом случае.