В чем разница между выражениями запроса 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();
Ответы
Ответ 1
Обновление: вы исправили свой заголовок, поэтому игнорируйте напыщенность.
Название вашего вопроса не имеет ничего общего с вашими образцами кода. Ваш вопрос подразумевает, что один синтаксис IEnumerable, а другой - IQueryable, но это неверно. В ваших выборках, если db.Surveys
- это IQueryable, то оба образца используют IQueryable. Я постараюсь ответить на оба вопроса.
Ваши два примера кода - это просто разные способы написания тех же запросов LINQ (при условии, что они хорошо написаны). Код в примере 1 является просто сокращением кода в примере 2. Компилятор обрабатывает код в обоих образцах одинаковым образом. Подумайте, как компилятор С# будет обрабатывать int?
так же, как Nullable<System.Int32>
. И язык С# и VB.Net предоставляет этот короткий синтаксис запроса. Другие языки могут не иметь этого синтаксиса, и вам придется использовать синтаксис образца 2. На самом деле, другие языки могут даже не поддерживать методы расширения или лямбда-выражения, и вам придется использовать еще более уродливый синтаксис.
Update:
Чтобы взять пример Sander дальше, когда вы пишете это (синтаксис понимания запроса):
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 не поддерживает синтаксис запроса, lambdas или методы расширения, это как-то так, как вам придется писать код самостоятельно.
Каковы преимущества использования одного стиля над другим?
Для небольших запросов методы расширения могут быть более компактными:
var items = source.Where(s => s > 5);
Кроме того, синтаксис метода расширения может быть более гибким, например условным, если предложения:
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 для объектов за кулисами (LINQ to XML, LINQ to DataSet).
IQueryable используется LINQ to SQL, LINQ to Entities и другими поставщиками LINQ, которые должны проверять ваш запрос и переводить его вместо прямого выполнения кода. Запросы IQueryable и их Expression<Func<TIn,TOut>>
не компилируются в IL во время компиляции. Вместо этого создается дерево выражений и может быть проверено во время выполнения. Это позволяет выполнять преобразования операторов на другие языки запросов (например, T-SQL). Дерево выражений может быть скомпилировано в Func < TIn, TOut > во время выполнения и выполняться при желании.
Пример, иллюстрирующий разницу, можно найти в этом вопросе, где OP хочет выполнить часть запроса LINQ to SQL в SQL Server, привести объекты в и выполнить остальную часть запроса в LINQ to Objects. Для этого все, что ему нужно сделать, это включить IQueryable в IEnumerable, где он хочет, чтобы коммутатор произошел.
Ответ 2
LINQ - это звуковое слово для технологии.
IQueryable - это .NET-интерфейс, который используется LINQ.
Помимо стиля, между ними нет никакой разницы. Используйте тот стиль, который вы предпочитаете.
Я предпочитаю первый стиль для длинного оператора (как показано здесь), а второй для очень коротких операторов.
Ответ 3
Предложение 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" просто ищет метод Where в вашем коде, который принимает Func.
Ответ 4
Выражения запроса и методы расширения - это два способа сделать то же самое. Выражения запроса преобразуются в методы расширения при компиляции - это просто синтаксический сахар для людей, которым удобнее работать с SQL.
Когда вы пишете это:
var surveyNames = from s in db.Surveys select s.Name;
Компилятор преобразует это в:
IQueryable<string> surveryNames = db.Surveys.Select(s => s.Name);
Действительно, я думаю, что выражения запроса были созданы только по соображениям маркетинга - SQL-подобная языковая конструкция, которая могла бы использоваться в качестве уловителя при создании LINQ, а не в том, что предлагает много фактического использования. Я считаю, что большинство людей просто используют методы расширения напрямую, поскольку они приводят к более унифицированному стилю кодирования, а не к сочетанию С# и SQL.
Ответ 5
1./Заголовок вопроса не соответствует тому, что вы просили.
2./Заголовок вопроса не имеет смысла. Linq означает Language Integrated Query и является зонтичным термином для множества технологий и практик, IQueryable - это интерфейс, который обычно используется для облегчения Linq. вы сравниваете Яблоки и Апельсины
3./О вашем фактическом вопросе, основное отличие - стиль, для сложных запросов, подобных этой, моим личным предпочтением является вторая версия, так как она четко показывает прогрессирование наборов результатов.
Ответ 6
Ваш 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;
Выражения используются, когда запрос менее сложный
Ответ 7
Я думаю, что ваш вопрос лучше сформулирован так: "В чем разница между IEnumerable <T> и IQueryable <T> по отношению к LINQ"
Запросы LINQ возвращают IQueryable <T> по умолчанию. IQueryable < Т > позволяет добавлять к вашему запросу другие фильтры или "предложения", прежде чем выполнять их.
Ваш запрос LINQ (первый пример) и ваш LINQ, используя цепочку методов (второй пример), дают одинаковый результат с различным синтаксисом.
Можно написать запрос LINQ как цепочку методов LINQ и наоборот. Это зависит от ваших предпочтений.
@Lucas: другое значение IEnumerable <T> выполняет запрос в памяти и IQueryable <T> делает из памяти. Значение, когда вы находитесь в итераторе foreach
, вы используете IEnumerable, а когда вы строите свой запрос, используя либо методы расширения, либо используя LINQ from o in object
synatax, вы создаете IQueryable <T> . IQueryable <T> выполняется, как только вы касаетесь Enumerator.
Ответ 8
Еще один момент, который стоит упомянуть, заключается в том, что методы расширения Linq придерживаются языка С#, тогда как материал для анализа запросов предварительно обрабатывается, как встроенный в компилятор.
вы можете перейти к определению .Select(x = > тогда как вы не можете использовать from ... where ... select