2010-07-22 26 views
42

Dada la siguiente LINQ a SQL consulta:La comprensión .AsEnumerable() en LINQ to SQL

var test = from i in Imports 
      where i.IsActive 
      select i; 

La sentencia SQL es interpretado:

SELECT [t0].[id] AS [Id] .... FROM [Imports] AS [t0] WHERE [t0].[isActive] = 1 

dicho que quiera llevar a cabo alguna acción en el selecto que no se puede convertir a SQL. Entiendo que la forma convencional de lograr esto es hacer AsEnumerable() convirtiéndolo así en un objeto viable.

Dado este código actualizado:

var test = from i in Imports.AsEnumerable() 
      where i.IsActive 
      select new 
      { 
       // Make some method call 
      }; 

y SQL actualización:

SELECT [t0].[id] AS [Id] ... FROM [Imports] AS [t0] 

Aviso la falta de una cláusula where de la instrucción SQL ejecutada.

¿Significa esto que toda la tabla "Importaciones" está almacenada en la memoria caché? ¿Disminuiría este rendimiento si la tabla contuviera una gran cantidad de registros?

Ayúdame a entender lo que está sucediendo detrás de escena aquí.

+1

¡Excelente pregunta! – fernandoespinosa

+0

Tener un vistazo a los ejemplos de otra pregunta: ¿ [enter Descripción enlace aquí] [1] [1]: http://stackoverflow.com/a/14129116/1792434 – lukaszk

Respuesta

33

La razón de AsEnumerable es

AsEnumerable (TSource) (IEnumerable (TSource)) se puede utilizar para elegir entre consulta implementaciones cuando una secuencia implementa IEnumerable (T), pero también tiene una un conjunto diferente de métodos de consulta pública disponibles

Así que cuando estuviera llamando el método Donde antes, que se llama a un método diferente, donde desde el IEnume rable.Dónde. Esa declaración Donde era para LINQ convertir a SQL, el nuevo ¿Dónde está el IEnumerable que toma un IEnumerable, lo enumera y cede los elementos coincidentes. Lo que explica por qué ves los diferentes SQL que se generan. La tabla se tomará en su totalidad de la base de datos antes de que se aplique la extensión Where en su segunda versión del código. Esto podría crear un cuello de botella grave, porque toda la tabla debe estar en la memoria, o peor, toda la mesa debería viajar entre servidores. Permita que SQL Server ejecute Where y haga lo que mejor hace.

+1

por lo que todavía ha diferido la ejecución ¿verdad? Usando la consulta anterior como ejemplo, toda la tabla "Importaciones" no se almacenará en la memoria solo las activas, cuando hago algo como "test.Dump()". ¿Es correcto? –

+0

@Mike Fielden Bueno, toda la tabla irá a través de la memoria como el lugar donde está enumerando. No estoy seguro de si todas las filas se cargarán en la memoria al mismo tiempo. –

+0

@YuriyFaktorovich: Entonces, ¿por qué existe? Amablemente dígales a los profesionales que también usan AsEnumerable. Gracias – Unbreakable

1

Creo que AsEnumerable solo le dice al compilador qué métodos de extensión usar (en este caso, los definidos para IEnumerable en lugar de los de IQueryable). La ejecución de la consulta aún se aplaza hasta que llame a ToArray o enumere en ella.

+0

Esto no tiene ningún sentido. AsEnumerable es un método. No "le dice al compilador" nada en particular. –

+2

Sí, es un método, pero su tipo de devolución es Enumerable, por lo tanto, el compilador utilizará métodos diferentes al compilar la consulta LINQ (los enumerables). De la documentación: El método AsEnumerable (IEnumerable ) no tiene otro efecto que cambiar el tipo de fuente de tiempo de compilación de un tipo que implementa IEnumerable a IEnumerable . http://msdn.microsoft.com/en-us/library/bb335435.aspx – Razvi

6

En el punto donde se enumera la enumeración, se consultará la base de datos y se recuperará todo el conjunto de resultados.

Una solución de parte y parte puede ser el camino. Considere

var res = (
    from result in SomeSource 
    where DatabaseConvertableCriterion(result) 
    && NonDatabaseConvertableCriterion(result) 
    select new {result.A, result.B} 
); 

Digamos también que NonDatabaseConvertableCriterion requiere el campo C del resultado. Como NonDatabaseConvertableCriterion hace lo que sugiere su nombre, esto debe realizarse como una enumeración.Sin embargo, tenga en cuenta:

var partWay = 
(
    from result in SomeSource 
    where DatabaseConvertableCriterion(result) 
    select new {result.A, result.B, result.C} 
); 
var res = 
(
    from result in partWay.AsEnumerable() 
    where NonDatabaseConvertableCriterion select new {result.A, result.B} 
); 

En este caso, cuando se enumera res, consultan o no utilizados, tanto trabajo como sea posible se pasará a la base de datos, que devolverá suficiente para continuar el trabajo. Suponiendo que realmente es realmente imposible reescribir para que todo el trabajo pueda enviarse a la base de datos, esto puede ser un compromiso adecuado.

4

Hay tres implementaciones de AsEnumerable.

DataTableExtensions.AsEnumerable

extiende una DataTable para darle una interfaz IEnumerable esta manera puede utilizar LINQ contra la DataTable.

Enumerable.AsEnumerable<TSource> y ParallelEnumerable.AsEnumerable<TSource>

El método AsEnumerable<TSource>(IEnumerable<TSource>) no tiene ningún efecto distinto para cambiar el tipo de tiempo de compilación de la fuente de un tipo que implementa IEnumerable<T>-IEnumerable<T> sí mismo.

AsEnumerable<TSource>(IEnumerable<TSource>) se puede utilizar para elegir entre las implementaciones de consulta cuando se implementa una secuencia IEnumerable<T> pero también tiene un conjunto diferente de los métodos de consulta pública disponibles. Por ejemplo, dada una clase genérica Table que implementa IEnumerable<T> y tiene sus propios métodos tales como Where, Select, y SelectMany, una llamada a Where invocaría el Where método público de Table. Un tipo Table que representa una tabla de base de datos podría tener un método Where que toma el argumento de predicado como un árbol de expresión y convierte el árbol a SQL para su ejecución remota. Si no se desea la ejecución remota , por ejemplo, porque el predicado invoca un método local , el método AsEnumerable<TSource> se puede utilizar para ocultar los métodos personalizados y, en su lugar, pone a disposición los operadores de consulta estándar .

En otras palabras.

Si tengo una

IQueryable<X> sequence = ...; 

de un LinqProvider, como marco de la entidad, y lo hago,

sequence.Where(x => SomeUnusualPredicate(x)); 

esa consulta se compone y se ejecuta en el servidor. Esto fallará en tiempo de ejecución porque EntityFramework no sabe cómo convertir SomeUnusualPredicate en SQL.

Si quiero que para ejecutar la declaración con LINQ a objetos en su lugar, lo que hago,

sequence.AsEnumerable().Where(x => SomeUnusualPredicate(x)); 

ahora el servidor devolverá todos los datos y la Enumerable.Where de LINQ a objetos se utilizará en lugar de la consulta Implementación del proveedor.

No importa que Entity Framework no sepa cómo interpretar SomeUnusualPredicate, mi función se usará directamente. (Sin embargo, esto puede ser un enfoque ineficiente ya que todas las filas serán devueltas desde el servidor.)