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

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

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

Пример 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();

8 ответов


Update: вы исправили свой заголовок, поэтому игнорируйте тираду.

название вашего вопроса не имеет ничего общего с вашими образцами кода. Ваш вопрос подразумевает, что один синтаксис 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, etc.

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


Update: вы исправили свой заголовок, поэтому игнорируйте остальное...

теперь о вашем названии: что касается 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 для объектов за кулисами (LINQ to XML, LINQ to DataSet).

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

пример, иллюстрирующий разницу, можно найти в этот вопрос где 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 с набором stringprop в 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 в вашем коде, который принимает 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 означает интегрированный запрос языка и является зонтичным термином для множества технологий и практик, IQueryable-это интерфейс, который обычно используется для облегчения Linq. вы сравниваете яблоки и апельсины
3./ О вашем фактическом вопросе, основное различие-стиль, для сложных запросов, подобных этому, мое личное предпочтение-2-я версия, так как она четко показывает прогрессию результирующих наборов.


код пример sample 1 - это представление верхнего уровня Linq, оно более читаемо, и при компиляции оно будет преобразовано в дерево выражений i.e ваш Sample2.

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 и IQueryable относительно LINQ"

запросы LINQ возвращают IQueryable по умолчанию. IQueryable позволяет добавлять другие фильтры или" предложения " в запрос перед его выполнением.

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

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

@Lucas: другой-IEnumerable делает запрос в памяти, а IQueryable - из памяти. Смысл, как только вы находитесь в foreach итератор, вы используете IEnumerable, и когда вы строите свой запрос, с помощью методов расширения или с помощью LINQ from o in object synatax, вы создаете IQueryable. IQueryable выполняется, как только вы касаетесь Перечислитель.


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