В чем разница между выражениями запроса LINQ и методами расширения

StackOverflow https://stackoverflow.com/questions/796246

  •  18-09-2019
  •  | 
  •  

Вопрос

Ниже приведены два запроса, которые возвращают одни и те же данные.Кроме стиля, я не уверен, что лучше.

Какие факторы влияют на эти запросы?Каковы преимущества использования одного стиля по сравнению с другим?

Пример 1

var x = from s in db.Surveys
    join sq in db.Survey_Questions on s.ID equals sq.Survey_ID
    join q in db.Questions on sq.Question_ID equals q.ID
    join qg in db.Question_Groups on q.ID equals qg.Question_ID
    where s.Type_ID.Equals(typeID) & s.Type.Equals(type)
    select new { question = sq.Question, status = sq.Status, grp = qg };

Пример 2

var x = db.Surveys.Where(s => s.Type_ID.Equals(typeID) & s.Type.Equals(type))
              .Join(db.Survey_Questions,
                        s => s.ID,
                        sq => sq.Survey_ID,
                        (s, sq) => new
                        {
                            question = sq.Question,
                            status = sq.Status
                        })
              .Join(db.Question_Groups,
                        q => q.question.ID,
                        qg => qg.Question_ID,
                        (q, qg) => new
                        {
                            question = q.question,
                            status = q.status,
                            group = qg
                        }).ToList();
Это было полезно?

Решение

Обновлять:Вы закрепили свой титул, так что не обращайте внимания на напыщенную речь.

Название вашего вопроса не имеет ничего общего с вашими примерами кода.Ваш вопрос подразумевает, что один синтаксис — IEnumerable, а другой — IQueryable, но это неверно.В ваших образцах, если db.Surveys является IQueryable, тогда оба ваши образцы используют IQueryable.Я постараюсь ответить оба вопросы.

Два ваших примера кода — это просто разные способы написания одних и тех же запросов LINQ (при условии, что они написаны хорошо).Код в примере 1 — это просто сокращение кода в примере 2.Компилятор обрабатывает код в обоих примерах одинаково.Подумайте о том, как компилятор C# будет обрабатывать int? такой же как Nullable<System.Int32>.Оба языка C# и VB.Net предоставляют этот сокращенный синтаксис запроса.В других языках этот синтаксис может отсутствовать, и вам придется использовать синтаксис примера 2.Фактически, другие языки могут даже не поддерживать методы расширения или лямбда-выражения, и вам придется использовать еще более уродливый синтаксис.


Обновлять:

Если продолжить пример Сандера, когда вы пишете это (синтаксис понимания запроса):

var surveyNames = from s in db.Surveys select s.Name

Ты думать компилятор превращает это сокращение в следующее (методы расширения и лямбда-выражение):

IQueryable<string> surveryNames = db.Surveys.Select(s => s.Name);

Но на самом деле методы расширения и лямбда-выражения сами по себе являются сокращениями.Компиляторы выдают что-то вроде этого (не совсем, но просто для понимания):

Expression<Func<Survey, string>> selector = delegate(Survey s) { return s.Name; };
IQueryable<string> surveryNames = Queryable.Select(db.Surveys, selector);

Обратите внимание, что Select() это просто статический метод в Queryable сорт.Если ваш язык .NET не поддерживает синтаксис запросов, лямбда-выражения или методы расширения, вам придется писать код самостоятельно.


Каковы преимущества использования одного стиля перед другим?

Для небольших запросов методы расширения могут быть более компактными:

var items = source.Where(s => s > 5);

Кроме того, синтаксис метода расширения может быть более гибким, например, условные предложенияwhere:

var items = source.Where(s => s > 5);

if(smallerThanThen)
    items = items.Where(s => s < 10);
if(even)
    items = items.Where(s => (s % 2) == 0);

return items.OrderBy(s => s);

Кроме того, некоторые методы доступны только через синтаксис методов расширения (Count(), Aggregate(), Take(), Skip(), ToList(), ToArray() и т. д.), поэтому, если я буду использовать один из них, Обычно я пишу весь запрос в этом синтаксисе, чтобы избежать смешивания обоих синтаксисов.

var floridaCount = source.Count(s => s.State == "FL");

var items = source
            .Where(s => s > 5)
            .Skip(5)
            .Take(3)
            .ToList();

С другой стороны, когда запрос становится больше и сложнее, синтаксис его понимания может быть более понятным, особенно если вы начнете усложнять его несколькими let, group, join, и т. д.

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


Обновлять:вы исправили свой заголовок, так что игнорируйте остальное...

Теперь о вашем названии:Что касается LINQ, IEnumerable и IQueryable очень похожи.Оба они имеют практически одинаковые методы расширения (Select, Where, Count и т. д.), при этом основное (единственное?) отличие состоит в том, что IEnumerable принимает Func<TIn,TOut> в качестве параметров и IQueryable принимает Expression<Func<TIn,TOut>> в качестве параметров.Вы выражаете оба одинаково (обычно ламба-выражения), но внутренне они совершенно разные.

IEnumerable — это дверь в LINQ to Objects.Методы расширения LINQ to Objects можно вызывать для любого IEnumerable (массивы, списки, все, что вы можете перебирать с помощью foreach) и Func<TIn,TOut> преобразуется в IL во время компиляции и запускается как обычный код метода во время выполнения.Обратите внимание, что некоторые другие поставщики LINQ используют IEnumerable и поэтому фактически используют LINQ to Objects (LINQ to XML, LINQ to DataSet).

IQueryable используется LINQ to SQL, LINQ to Entities и другими поставщиками LINQ, которым необходимо изучить ваш запрос и преобразовать его вместо прямого выполнения вашего кода.Запросы IQueryable и их Expression<Func<TIn,TOut>>s не компилируются в IL во время компиляции.Вместо этого дерево выражений создается и может быть проверен во время выполнения.Это позволяет переводить операторы на другие языки запросов (например, T-SQL).Дерево выражений можно скомпилировать в Func<TIn,TOut> во время выполнения и при необходимости выполнить.

Пример, иллюстрирующий разницу, можно найти в этот вопрос где OP хочет выполнить часть запроса LINQ to SQL в SQL Server, перенести объекты в управляемый код и выполнить остальную часть запроса в LINQ to Objects.Для этого все, что ему нужно сделать, — это преобразовать IQueryable в IEnumerable, где он хочет, чтобы произошло переключение.

Другие советы

LINQ — модное слово для обозначения технологии.

IQueryable — это интерфейс .NET, используемый LINQ.

Кроме стиля, между ними нет никакой разницы.Используйте тот стиль, который вы предпочитаете.

Я предпочитаю первый стиль для длинных заявлений (как показано здесь), а второй — для очень коротких заявлений.

Предложение «where» в первом примере на самом деле является просто синтаксическим сахаром для предложения «Where» во втором методе.Фактически, вы можете написать свой собственный класс, который не имеет ничего общего с Linq или IQueryable, и, просто имея метод Where, вы можете использовать этот синтаксический сахар.Например:

    public class MyClass
    {

        public MyClass Where<T>(Func<MyClass, T> predicate)
        {
            return new MyClass { StringProp = "Hello World" };
        }

        public MyClass Select<T>(Func<MyClass, T> predicate)
        {
            return new MyClass ();
        }



        public string StringProp { get; set; }
    }

Очевидно, это глупый пример, но обратите внимание, что существует метод Where, который просто возвращает новый MyClass со строковым свойством, установленным в Hello World.Демонстрировать:

MyClass a = new MyClass();
            var q = from p in a
                    where p.StringProp == "foo" // doesnt matter what we put here, as we're not really checking the predicate
                    select p;
            Console.WriteLine(q.StringProp);

Это приведет к написанию «Hello World».Опять же, этот пример явно бессмысленен, но он доказывает, что синтаксис «where» просто ищет в вашем коде метод Where, который принимает Func.

Выражения запроса и методы расширения — это два способа сделать одно и то же.Выражения запроса при компиляции преобразуются в методы расширения — они просто синтаксический сахар для людей, которые более комфортно работают с SQL.

Когда вы пишете это:

var surveyNames = from s in db.Surveys select s.Name;

Компилятор преобразует это в:

IQueryable<string> surveryNames = db.Surveys.Select(s => s.Name);

На самом деле, я думаю, что выражения запросов были созданы просто из маркетинговых соображений — это SQL-подобная языковая конструкция, призванная привлекать внимание при разработке LINQ, а не то, что предлагает широкое практическое использование.Я считаю, что большинство людей просто используют методы расширения напрямую, поскольку они приводят к более унифицированному стилю кодирования, а не к смеси C# и SQL.

1./ Название вашего вопроса не соответствует тому, что вы спросили.
2./ Название вашего вопроса не имеет смысла.Linq означает «Language Integrated Query» и является общим термином для множества технологий и практик. IQueryable — это интерфейс, который обычно используется для упрощения Linq.ты сравниваешь яблоки и апельсины
3./Что касается вашего вопроса, то основное отличие - это стиль. Для сложных запросов, подобных этому, я лично предпочитаю вторую версию, поскольку она четко показывает прогресс наборов результатов.

Твой Образец1 является представлением Linq верхнего уровня, оно более читабельно, и при компиляции оно преобразуется в дерево выражений, т.е. ваше Образец2.

var x = from s in db.Surveys
    join sq in db.Survey_Questions on s.ID equals sq.Survey_ID
    join q in db.Questions on sq.Question_ID equals q.ID
    join qg in db.Question_Groups on q.ID equals qg.Question_ID
    where s.Type_ID.Equals(typeID) & s.Type.Equals(type)
    select new { question = sq.Question, status = sq.Status, grp = qg };

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

var exp=x.Expression;

Выражения используются, когда запрос менее сложный.

Я думаю, что ваш вопрос лучше сформулировать так: «В чем разница между IEnumerable<T> и IQueryable<T> по отношению к LINQ»

Запросы LINQ по умолчанию возвращают IQueryable<T>.IQueryable<T> позволяет добавлять к запросу другие фильтры или «предложения» перед его выполнением.

Ваш запрос LINQ (первый пример) и LINQ с использованием цепочки методов (второй пример) дают один и тот же результат с разным синтаксисом.

Запрос LINQ можно написать в виде цепочки методов LINQ и наоборот.Это действительно зависит от ваших предпочтений.

@Лукас:Разница в том, что IEnumerable<T> выполняет запросы в памяти, а IQueryable<T> выполняет запросы вне памяти.Это означает, что как только вы окажетесь в foreach итератор, вы используете IEnumerable, и когда вы создаете свой запрос, либо с помощью методов расширения, либо с помощью LINQ from o in object Synatax, вы создаете IQueryable<T>.IQueryable<T> выполняется, как только вы касаетесь Enumerator.

Еще один момент, который стоит упомянуть, заключается в том, что методы расширения Linq соответствуют языку C #, в то время как материал для понимания запросов предварительно обрабатывается, как встроенный в компилятор.т.е. вы можете перейти к определению .Select(x => , тогда как вы не можете для from ... where ... select

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top