Какую коллекцию следует использовать в запросе linq-to-sql? Queryable vs Enumerable vs List

Представьте себе следующие классы:

class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

class Underage
{
    public int Age { get; set; }
}

И я делаю что-то вроде этого:

var underAge = db.Underage.Select(u => u.Age)   .ToList()/AsEnumerable()/AsQueryable() 

var result = db.Persons.Where(p => underAge.Contains(p.Age)).ToList();

Какой лучший вариант? Если я вызову ToList(), элементы будут получены один раз, но если я выберу AsEnumerable или AsQueryable, они будут выполняться каждый раз, когда человек будет выбран из базы данных в Where() (если это так, я не знаю, что такое база данных). делает на заднем плане)?

Также есть ли большая разница, когда несовершеннолетние будут содержать тысячи записей по сравнению с небольшим количеством?


person Alexander Derck    schedule 25.11.2015    source источник


Ответы (3)


Никто.

Вам определенно не нужен ToList() здесь, так как это загрузило бы все совпадающие значения в память, а затем использовало бы его для написания запроса для result, в котором было бы массивное предложение IN (…). Это пустая трата, когда вы действительно хотите просто получить совпадающие значения из базы данных в первую очередь с помощью запроса, который выглядит примерно так:

SELECT *
FROM Persons
WHERE EXISTS(
  SELECT NULL FROM 
  FROM Underage
  WHERE Underage.age = Persons.age
)

AsEnumerable() часто препятствует выполнению каких-либо действий в базе данных, хотя провайдеры, скорее всего, изучат источник и отменят последствия этого AsEnumerable(). AsQueryable() подходит, но в данном случае ничего не делает.

Вам не нужен ни один из них:

var underAge = db.Underage.Select(u => u.Age);

var result = db.Persons.Where(p => underAge.Contains(p.Age)).ToList();

(Если на то пошло, убедитесь, что вам действительно нужен этот ToList() в последней строке; если вы собираетесь делать дальнейшие запросы к нему, это, вероятно, навредит им, если вы просто собираетесь перечислять результаты, это пустая трата времени. времени и памяти, если вы собираетесь хранить их или выполнять множество операций в памяти, которые нельзя выполнить в Linq, это будет к лучшему).

person Jon Hanna    schedule 25.11.2015
comment
Итак, когда я вызываю ToList во второй строке, моя программа выполнит только один запрос? - person Alexander Derck; 25.11.2015
comment
Да, выполните его один раз, сделайте примерно то же самое, что показано выше, и создайте список. Однако если вы просто выполнили foreach для него, он выполнит SQL, а затем будет использовать каждый объект по мере его поступления, что зачастую происходит быстрее. Если бы вы не сделали ToList() или foreach, то у вас был бы другой запрос, который можно было бы составить еще дальше. Например. добавив еще один Where() или вызвав что-то вроде Count(), где большая часть работы может быть выполнена в базе данных, а не захватывать ее всю. - person Jon Hanna; 25.11.2015
comment
Спасибо! Я посмотрю на этот foreach, никогда не использовал его с Linq - person Alexander Derck; 25.11.2015
comment
В 98% случаев, если это вообще возможно, ваши конечные результаты с linq должны либо вызывать агрегат (Count(), Max() или что-то еще, что дает один результат), либо просто foreach напрямую через результаты. Вызывайте метод создания коллекции (ToList(), ToDictionary(), ToLookup()), только если вам действительно нужна эта коллекция. Есть некоторые исключения (в частности, некоторое использование GroupBy будет более эффективным в памяти), но по умолчанию всегда извлекается столько памяти, сколько вам нужно. - person Jon Hanna; 25.11.2015
comment
Общее правило — сохранять объект как можно ближе к оригиналу как можно дольше. DbSet › IQueryable › IEnumerable › Список/массив. Используйте один из более низких только тогда, когда есть причина, по которой вам нужен более низкий, а более высокий не будет работать. - person Robert McKee; 25.11.2015
comment
Отличный совет, буду иметь в виду, когда буду работать с базами данных :) - person Alexander Derck; 26.11.2015

Если вы просто оставите свой первый запрос как

  var underage = db.Underage.Select(u => u.Age);

Это будет тип IQueryable, и он не будет пинговать вашу базу данных (фактически это называется отложенным выполнением). Если вы действительно хотите выполнить вызов базы данных, вы можете использовать жадный оператор, такой как .ToList(), .ToArray(), .ToDictionary(). Это даст вашей результирующей переменной коллекцию IEnumerable.

см. отложенное выполнение linq

person mrsargent    schedule 25.11.2015
comment
Спасибо! Есть ли разница между тем, чтобы оставить такой код и добавить .AsQueryable()? я полагаю, нет - person Alexander Derck; 25.11.2015
comment
Только то, что добавление AsQueryable() может запутать проблемы, когда вы позже посмотрите на код и задаетесь вопросом, для чего он был там. - person Jon Hanna; 25.11.2015

Вы должны использовать соединение для получения данных.

var query = (from p in db.Persons
             join u in db.Underage
              on u.Age equals p.Age
             select p).ToList();

Если вы не используете .ToList() в приведенном выше запросе, он возвращает IEnumerable типа Person, и фактические данные будут получены при использовании объекта.

Все они одинаково хороши ToList(), AsEnurmeable и Querable в зависимости от сценария, который вы хотите использовать. В вашем случае ToList выглядит хорошо для меня, так как вы просто хотите получить список несовершеннолетних.

person Neha Jain    schedule 25.11.2015
comment
Отредактировал, но хочу знать, что лучше в моем примере выше с выражениями Linq. - person Alexander Derck; 25.11.2015
comment
Должна быть разница. Если я сделаю его IQueryable, он «добавит» этот запрос ко второму запросу или сделает отдельный запрос, например, когда я буду использовать ToList()? - person Alexander Derck; 25.11.2015
comment
Присоединение было бы неверным, если бы в underage были повторяющиеся возрасты. Чтобы сопоставить результаты запроса в вопросе, вы должны либо иметь его как известный инвариант, что нет повторяющихся возрастов, либо использовать Distinct() для этого. - person Jon Hanna; 25.11.2015