В чем разница между выражениями запросов LINQ и методами расширения - PullRequest
13 голосов
/ 28 апреля 2009

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

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

Образец 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 ]

21 голосов
/ 28 апреля 2009

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

Название вашего вопроса не имеет ничего общего с вашими примерами кода. Ваш вопрос подразумевает, что один синтаксис 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>> в качестве параметров. Вы выражаете оба одинаково (обычно выражения lamba), но внутренне они совершенно разные.

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>> не компилируются в IL во время компиляции. Вместо этого создается дерево выражений , которое можно просмотреть во время выполнения. Это позволяет переводить операторы на другие языки запросов (например, T-SQL). Дерево выражений может быть скомпилировано в Func во время выполнения и выполнено при желании.

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

3 голосов
/ 28 апреля 2009

LINQ - модное слово для технологии.

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

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

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

2 голосов
/ 28 апреля 2009

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

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

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

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

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

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

2 голосов
/ 28 апреля 2009

Предложение 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.

1 голос
/ 28 апреля 2009

Ваш Sample1 - представление Linq верхнего уровня, он более читабелен, и во время компиляции он преобразуется в дерево выражений, т.е. ваш 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;

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

1 голос
/ 28 апреля 2009

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

0 голосов
/ 23 июня 2015

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

0 голосов
/ 28 апреля 2009

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...