usar que implement custom como c# linq iqueryable

que - linq c# custom



Usando IQueryable con Linq (4)

Aunque Reed Copsey y Marc Gravell ya describieron suficientemente IEnumerable (y también IEnumerable ), quiero agregar un poco más aquí al proporcionar un pequeño ejemplo en IEnumerable e IQueryable ya que muchos usuarios lo solicitaron.

Ejemplo : he creado dos tablas en la base de datos

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)

La clave principal ( PersonId ) de la tabla Employee también es una clave personid ( personid ) de la tabla Person

A continuación, agregué el modelo de entidad ado.net en mi aplicación y creé la siguiente clase de servicio en ese

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; } }

contienen la misma linq. Llamó en program.cs como se define a continuación

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(); } }

La salida es igual para ambos, obviamente.

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

Entonces la pregunta es ¿cuál es la diferencia? No parece haber ninguna diferencia, ¿verdad? ¡¡De Verdad!!

Echemos un vistazo a las consultas de sql generadas y ejecutadas por la entidad framwork 5 durante este período

Pieza de ejecución 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 parte de ejecución

--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)

Guión común para ambas partes de ejecución

/* 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 */

Así que ahora tienes pocas preguntas, déjame adivinar esas e intenta responderlas.

¿Por qué se generan diferentes scripts para el mismo resultado?

Vamos a descubrir algunos puntos aquí,

Todas las consultas tienen una parte común.

WHERE [Extent1].[PersonId] IN (0,1,2,3)

¿por qué? Debido a que ambas funciones IQueryable<Employee> GetEmployeeAndPersonDetailIQueryable e IEnumerable<Employee> GetEmployeeAndPersonDetailIEnumerable de SomeServiceClass contiene una línea común en las consultas de linq

where employeesToCollect.Contains(e.PersonId)

AND (N''M'' = [Extent1].[Gender]) ¿por qué AND (N''M'' = [Extent1].[Gender]) la parte AND (N''M'' = [Extent1].[Gender]) parte de ejecución de IEnumerable , mientras que en la llamada de ambas funciones utilizamos Where(i => i.Gender == "M") in programa .cs`

Ahora estamos en el punto de diferencia entre IEnumerable y IQueryable IEnumerable

La estructura de la entidad hace cuando se llama un método IQueryable, tomó la instrucción linq escrita dentro del método y trata de averiguar si hay más expresiones linq definidas en el conjunto de resultados, luego reúne todas las consultas linq definidas hasta que el resultado deba buscarse y construirse de manera más apropiada. Consulta SQL para ejecutar.

Proporciona un montón de beneficios como,

  • solo aquellas filas rellenadas por el servidor sql que podrían ser válidas por toda la ejecución de la consulta linq
  • Ayuda al rendimiento del servidor SQL al no seleccionar filas innecesarias
  • costo de red conseguir reducir

como aquí en el ejemplo, el servidor sql devolvió a la aplicación solo dos filas después de la ejecución de IQueryable` pero devolvió TRES filas para la consulta IEnumerable ¿por qué?

En el caso del método IEnumerable , el marco de la entidad tomó la instrucción linq escrita dentro del método y construye la consulta SQL cuando se necesita obtener el resultado. no incluye la parte de linq de descanso para construir la consulta de SQL. Como aquí, no se realiza ningún filtrado en el servidor SQL en la columna de gender .

Pero las salidas son las mismas? Porque ''IEnumerable filtra aún más el resultado en el nivel de aplicación después de recuperar el resultado del servidor SQL

Entonces, ¿qué debería elegir alguien? Personalmente prefiero definir el resultado de la función como IQueryable<T> porque hay muchos beneficios que tiene sobre IEnumerable , como por IEnumerable , puedes unir dos o más funciones de IQueryable, que generan un script más específico para el servidor SQL.

Aquí, en el ejemplo, puede ver que una IQueryable Query(IQueryableQuery2) IEnumerable query(IEnumerableQuery2) genera un script más específico que la IEnumerable query(IEnumerableQuery2) que es mucho más aceptable en mi punto de vista.

¿Cuál es el uso de IQueryable en el contexto de LINQ?

¿Se utiliza para desarrollar métodos de extensión o cualquier otro propósito?


En esencia, su trabajo es muy similar a IEnumerable<T> - para representar un origen de datos consultable - la diferencia es que los diversos métodos LINQ (en Queryable ) pueden ser más específicos, para construir la consulta utilizando árboles de Expression lugar de delegados (que es lo que utiliza Enumerable ).

El proveedor de LINQ elegido puede inspeccionar los árboles de expresiones y convertirlos en una consulta real , aunque eso es un arte negro en sí mismo.

Esto se debe realmente a ElementType , Expression y Provider , pero en realidad rara vez debe preocuparse por esto como usuario . Solo un implementador de LINQ necesita conocer los detalles sangrientos.

Re comentarios; No estoy muy seguro de lo que quiere a modo de ejemplo, pero considere LINQ-to-SQL; El objeto central aquí es un DataContext , que representa nuestra envoltura de base de datos. Normalmente, esto tiene una propiedad por tabla (por ejemplo, Customers ), y una tabla implementa IQueryable<Customer> . Pero no usamos mucho directamente; considerar:

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); } }

esto se convierte (por el compilador de C #):

var qry = ctx.Customers.Where(cust => cust.Region == "North") .Select(cust => new { cust.Id, cust.Name });

que se interpreta de nuevo (por el compilador de C #) como:

var qry = Queryable.Select( Queryable.Where( ctx.Customers, cust => cust.Region == "North"), cust => new { cust.Id, cust.Name });

Es importante destacar que los métodos estáticos en Queryable toman árboles de expresión, que, en lugar de IL normal, se compilan en un modelo de objeto. Por ejemplo, solo mirando el "Dónde", esto nos da algo comparable a:

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) ...

¿El compilador no hizo mucho por nosotros? Este modelo de objeto se puede desgarrar, inspeccionar por su significado y volver a armarlo con el generador de TSQL, dando algo como:

SELECT c.Id, c.Name FROM [dbo].[Customer] c WHERE c.Region = ''North''

(la cadena podría terminar como un parámetro; no puedo recordar)

Nada de esto sería posible si hubiésemos utilizado un delegado. Y este es el punto de Queryable / Queryable IQueryable<T> : proporciona el punto de entrada para usar árboles de expresiones.

Todo esto es muy complejo, por lo que es un buen trabajo que el compilador lo haga agradable y fácil para nosotros.

Para obtener más información, consulte " C # en profundidad " o " LINQ en acción ", los cuales brindan cobertura de estos temas.


Permite una mayor consulta más adelante en la línea. Si esto fuera más allá de un límite de servicio, entonces el usuario de este objeto IQueryable podría hacer más con él.

Por ejemplo, si estaba usando la carga diferida con nhibernate, esto podría resultar en la carga del gráfico cuando sea necesario.


La respuesta de Marc Gravell es muy completa, pero también pensé que agregaría algo sobre esto desde el punto de vista del usuario ...

La principal diferencia, desde la perspectiva del usuario, es que, cuando utiliza IQueryable<T> (con un proveedor que admite las cosas correctamente), puede ahorrar muchos recursos.

Por ejemplo, si está trabajando contra una base de datos remota, con muchos sistemas ORM, tiene la opción de obtener datos de una tabla de dos maneras, una que devuelve IEnumerable<T> y otra que devuelve una IQueryable<T> . Digamos, por ejemplo, que tiene una tabla de productos y desea obtener todos los productos cuyo costo es> $ 25.

Si lo haces:

IEnumerable<Product> products = myORM.GetProducts(); var productsOver25 = products.Where(p => p.Cost >= 25.00);

Lo que sucede aquí es que la base de datos carga todos los productos y los transfiere a su programa. Su programa luego filtra los datos. En esencia, la base de datos hace un SELECT * FROM Products y le devuelve CADA producto.

Con el proveedor IQueryable<T> correcto, por otro lado, puede hacer:

IQueryable<Product> products = myORM.GetQueryableProducts(); var productsOver25 = products.Where(p => p.Cost >= 25.00);

El código se ve igual, pero la diferencia aquí es que el SQL ejecutado será SELECT * FROM Products WHERE Cost >= 25 .

Desde tu POV como desarrollador, esto se ve igual. Sin embargo, desde un punto de vista de rendimiento, solo puede devolver 2 registros a través de la red en lugar de 20,000 ...