Использование IQueryable с Linq
зачем IQueryable
в контексте LINQ?
используется ли он для разработки методов расширения или любой другой цели?
4 ответа:
Марк Gravell это!--10--> очень полный, но я думал, что добавлю что-то об этом с точки зрения пользователя, а также...
основное отличие, с точки зрения пользователя, заключается в том, что при использовании
IQueryable<T>
(с поставщиком, который поддерживает вещи правильно), вы можете сэкономить много ресурсов.например, если вы работаете с удаленной базой данных, с несколькими системами ORM, у вас есть возможность извлечения данных из таблицы в два пути, один из которых возвращает
IEnumerable<T>
, и тот, который возвращаетIQueryable<T>
. Скажем, например, у вас есть таблица продуктов, и вы хотите, чтобы получить все продукты, стоимость которых составляет >$25.если вы:
IEnumerable<Product> products = myORM.GetProducts(); var productsOver25 = products.Where(p => p.Cost >= 25.00);
что происходит здесь, - это база данных загружает все продукты, и передает их по сети к вашей программе. Затем ваша программа фильтрует данные. По сути, база данных делает a
SELECT * FROM Products
, и возвращает каждый продукт к вам.право
IQueryable<T>
поставщик, с другой стороны, вы можете сделать:IQueryable<Product> products = myORM.GetQueryableProducts(); var productsOver25 = products.Where(p => p.Cost >= 25.00);
код выглядит так же, но разница здесь в том, что SQL выполняется будет
SELECT * FROM Products WHERE Cost >= 25
.от вашего POV как разработчика, это выглядит так же. Однако с точки зрения производительности вы можете возвращать только 2 записи по сети вместо 20 000....
по сути своей его работа очень похожа на
IEnumerable<T>
- для представления запрашиваемого источника данных-разница в том, что различные методы LINQ (onQueryable
) может быть более конкретным, чтобы построить запрос с помощьюExpression
деревья, а не делегаты (что такоеEnumerable
использует).деревья выражений могут быть проверены выбранным поставщиком LINQ и превращены в фактический query-хотя это черная искусство само по себе.
это действительно до элемент
ElementType
,Expression
иProvider
- но на самом деле ты редко нужно заботиться об этом как пользователей. Только LINQ разработчик нужно знать кровавые детали.
Re комментарии; я не совсем уверен, что вы хотите в качестве примера, но рассмотрим LINQ-to-SQL; центральный объект здесь
DataContext
, который представляет нашу базу данных-обертку. Это обычно имеет свойство для каждой таблицы (например,Customers
), а таблица реализуетIQueryable<Customer>
. Но мы не используем это напрямую; рассмотрим:using(var ctx = new MyDataContext()) { var qry = from cust in ctx.Customers where cust.Region == "North" select new { cust.Id, cust.Name }; foreach(var row in qry) { Console.WriteLine("{0}: {1}", row.Id, row.Name); } }
это становится (компилятором C#):
var qry = ctx.Customers.Where(cust => cust.Region == "North") .Select(cust => new { cust.Id, cust.Name });
который снова интерпретируется (компилятором C#) как:
var qry = Queryable.Select( Queryable.Where( ctx.Customers, cust => cust.Region == "North"), cust => new { cust.Id, cust.Name });
важно, что статические методы на
Queryable
возьмите деревья выражений, которые-вместо обычного IL, компилируются в объектную модель. Например, просто глядя На "где", это дает нам что-то сравнимое с:var cust = Expression.Parameter(typeof(Customer), "cust"); var lambda = Expression.Lambda<Func<Customer,bool>>( Expression.Equal( Expression.Property(cust, "Region"), Expression.Constant("North") ), cust); ... Queryable.Where(ctx.Customers, lambda) ...
разве компилятор не сделал много для нас? Эта объектная модель может быть разорвана, проверена на то, что она означает, и снова собрана генератором TSQL - давая что-то вроде:
SELECT c.Id, c.Name FROM [dbo].[Customer] c WHERE c.Region = 'North'
(строка может оказаться параметром; я не могу вспомнить)
все это было бы возможно, если бы мы просто использовали делегат. И этой смысл
Queryable
/IQueryable<T>
: он обеспечивает точку входа для использования деревьев выражений.все это очень сложно, так что это это хорошая работа, что компилятор делает его приятным и легким для нас.
для получения дополнительной информации, посмотрите на "C# в глубину" или "LINQ в действии", оба из которых обеспечивают освещение этой тематики.
хотя Рид Copsey и Марк Gravell уже описал о
IQueryable
(а такжеIEnumerable
) достаточно, я хочу добавить немного больше здесь, предоставив небольшой пример наIQueryable
иIEnumerable
как многие пользователи просилипример: я создал две таблицы в базе данных
CREATE TABLE [dbo].[Employee]([PersonId] [int] NOT NULL PRIMARY KEY,[Gender] [nchar](1) NOT NULL) CREATE TABLE [dbo].[Person]([PersonId] [int] NOT NULL PRIMARY KEY,[FirstName] [nvarchar](50) NOT NULL,[LastName] [nvarchar](50) NOT NULL)
первичным ключом(
PersonId
) таблицыEmployee
также является ключом forgein (personid
) таблицыPerson
далее я добавлено ado.net модель сущности в моем приложении и создать ниже класса обслуживания на этом
public class SomeServiceClass { public IQueryable<Employee> GetEmployeeAndPersonDetailIQueryable(IEnumerable<int> employeesToCollect) { DemoIQueryableEntities db = new DemoIQueryableEntities(); var allDetails = from Employee e in db.Employees join Person p in db.People on e.PersonId equals p.PersonId where employeesToCollect.Contains(e.PersonId) select e; return allDetails; } public IEnumerable<Employee> GetEmployeeAndPersonDetailIEnumerable(IEnumerable<int> employeesToCollect) { DemoIQueryableEntities db = new DemoIQueryableEntities(); var allDetails = from Employee e in db.Employees join Person p in db.People on e.PersonId equals p.PersonId where employeesToCollect.Contains(e.PersonId) select e; return allDetails; } }
они содержат тот же linq. Он позвонил в
program.cs
как определено нижеclass Program { static void Main(string[] args) { SomeServiceClass s= new SomeServiceClass(); var employeesToCollect= new []{0,1,2,3}; //IQueryable execution part var IQueryableList = s.GetEmployeeAndPersonDetailIQueryable(employeesToCollect).Where(i => i.Gender=="M"); foreach (var emp in IQueryableList) { System.Console.WriteLine("ID:{0}, EName:{1},Gender:{2}", emp.PersonId, emp.Person.FirstName, emp.Gender); } System.Console.WriteLine("IQueryable contain {0} row in result set", IQueryableList.Count()); //IEnumerable execution part var IEnumerableList = s.GetEmployeeAndPersonDetailIEnumerable(employeesToCollect).Where(i => i.Gender == "M"); foreach (var emp in IEnumerableList) { System.Console.WriteLine("ID:{0}, EName:{1},Gender:{2}", emp.PersonId, emp.Person.FirstName, emp.Gender); } System.Console.WriteLine("IEnumerable contain {0} row in result set", IEnumerableList.Count()); Console.ReadKey(); } }
выход одинаковый для обоих очевидно
ID:1, EName:Ken,Gender:M ID:3, EName:Roberto,Gender:M IQueryable contain 2 row in result set ID:1, EName:Ken,Gender:M ID:3, EName:Roberto,Gender:M IEnumerable contain 2 row in result set
Итак, вопрос в том, что / где разница? Похоже, что нет есть ли разница, верно? Правда!!
давайте посмотрим на sql-запросы, созданные и выполняемые сущностью проводился 5 за эти точка
IQueryable часть выполнения
--IQueryableQuery1 SELECT [Extent1].[PersonId] AS [PersonId], [Extent1].[Gender] AS [Gender] FROM [dbo].[Employee] AS [Extent1] WHERE ([Extent1].[PersonId] IN (0,1,2,3)) AND (N'M' = [Extent1].[Gender]) --IQueryableQuery2 SELECT [GroupBy1].[A1] AS [C1] FROM ( SELECT COUNT(1) AS [A1] FROM [dbo].[Employee] AS [Extent1] WHERE ([Extent1].[PersonId] IN (0,1,2,3)) AND (N'M' = [Extent1].[Gender]) ) AS [GroupBy1]
IEnumerable часть выполнения
--IEnumerableQuery1 SELECT [Extent1].[PersonId] AS [PersonId], [Extent1].[Gender] AS [Gender] FROM [dbo].[Employee] AS [Extent1] WHERE [Extent1].[PersonId] IN (0,1,2,3) --IEnumerableQuery2 SELECT [Extent1].[PersonId] AS [PersonId], [Extent1].[Gender] AS [Gender] FROM [dbo].[Employee] AS [Extent1] WHERE [Extent1].[PersonId] IN (0,1,2,3)
общий скрипт для выполнения части
/* these two query will execute for both IQueryable or IEnumerable to get details from Person table Ignore these two queries here because it has nothing to do with IQueryable vs IEnumerable --ICommonQuery1 exec sp_executesql N'SELECT [Extent1].[PersonId] AS [PersonId], [Extent1].[FirstName] AS [FirstName], [Extent1].[LastName] AS [LastName] FROM [dbo].[Person] AS [Extent1] WHERE [Extent1].[PersonId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=1 --ICommonQuery2 exec sp_executesql N'SELECT [Extent1].[PersonId] AS [PersonId], [Extent1].[FirstName] AS [FirstName], [Extent1].[LastName] AS [LastName] FROM [dbo].[Person] AS [Extent1] WHERE [Extent1].[PersonId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=3 */
Итак, у вас есть несколько вопросов сейчас, позвольте мне угадать их и попытаться ответить на них
почему разные скрипты генерируются для одного и того же результата?
Давайте узнаем некоторые моменты здесь,
все запросы имеют одну общую часть
WHERE [Extent1].[PersonId] IN (0,1,2,3)
почему? Потому что обе функции
IQueryable<Employee> GetEmployeeAndPersonDetailIQueryable
иIEnumerable<Employee> GetEmployeeAndPersonDetailIEnumerable
наSomeServiceClass
содержит одну общую строку в запросах linq
where employeesToCollect.Contains(e.PersonId)
чем это
AND (N'M' = [Extent1].[Gender])
часть отсутствует вIEnumerable
часть выполнения, в то время как в обоих вызовах функции мы использовали
Это позволяет для дальнейшего запроса дальше по линии. Если бы это было за пределами границы службы, то пользователю этого объекта IQueryable было бы разрешено делать с ним больше.
например, если вы использовали ленивую загрузку с nhibernate, это может привести к загрузке графика, когда/если это необходимо.