Étant donné la requête LINQ to SQL suivante:
var test = from i in Imports
where i.IsActive
select i;
L'instruction SQL interprétée est:
SELECT [t0].[id] AS [Id] .... FROM [Imports] AS [t0] WHERE [t0].[isActive] = 1
Disons que je voulais effectuer une action dans la sélection qui ne peut pas être convertie en SQL. Je crois comprendre que la manière conventionnelle d'y parvenir est de faire AsEnumerable()
, le convertissant ainsi en un objet réalisable.
Compte tenu de ce code mis à jour:
var test = from i in Imports.AsEnumerable()
where i.IsActive
select new
{
// Make some method call
};
Et SQL mis à jour:
SELECT [t0].[id] AS [Id] ... FROM [Imports] AS [t0]
Notez l'absence d'une clause where dans l'instruction SQL exécutée.
Est-ce à dire que l'intégralité de la table "Imports" est mise en cache dans la mémoire? Est-ce que cela ralentirait les performances si la table contenait une grande quantité d'enregistrements?
Aidez-moi à comprendre ce qui se passe réellement dans les coulisses ici.
La raison de AsEnumerable est de
AsEnumerable (TSource) (IEnumerable (TSource)) peut être utilisé pour choisir entre les implémentations de requête lorsqu'une séquence implémente IEnumerable (T) mais dispose également d'un ensemble différent de méthodes de requête publiques disponibles
Ainsi, lorsque vous appeliez la méthode Where
auparavant, vous appeliez une méthode Where
différente de la IEnumerable.Where
. Cette instruction Where
était destinée à la conversion de LINQ en SQL, la nouvelle Where
est la IEnumerable
qui prend un IEnumerable
, l'énumère et donne les éléments correspondants . Ce qui explique pourquoi vous voyez les différents SQL générés. Le tableau sera entièrement extrait de la base de données avant que l'extension Where
soit appliquée dans votre deuxième version du code. Cela pourrait créer un goulot d'étranglement sérieux, car toute la table doit être en mémoire, ou pire, la table entière devrait voyager entre les serveurs. Autorisez SQL Server à exécuter le Where
et faites ce qu'il fait le mieux.
Au point où l'énumération est énumérée, la base de données sera alors interrogée et l'ensemble des résultats récupéré.
Une solution partielle peut être la solution. Considérer
var res = (
from result in SomeSource
where DatabaseConvertableCriterion(result)
&& NonDatabaseConvertableCriterion(result)
select new {result.A, result.B}
);
Disons également que NonDatabaseConvertableCriterion nécessite le champ C du résultat. Étant donné que NonDatabaseConvertableCriterion fait ce que son nom suggère, cela doit être effectué comme une énumération. Cependant, considérez:
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}
);
Dans ce cas, lorsque res est énuméré, interrogé ou utilisé d'une autre manière, autant de travail que possible sera transmis à la base de données, qui renverra suffisamment pour continuer le travail. En supposant qu'il est vraiment impossible de réécrire pour que tout le travail puisse être envoyé à la base de données, cela peut être un compromis approprié.
Il existe trois implémentations de AsEnumerable
.
DataTableExtensions.AsEnumerable
Étend un DataTable
pour lui donner une interface IEnumerable
afin que vous puissiez utiliser Linq contre le DataTable
.
Enumerable.AsEnumerable<TSource>
et ParallelEnumerable.AsEnumerable<TSource>
La méthode
AsEnumerable<TSource>(IEnumerable<TSource>)
n'a d'autre effet que de changer le type de source au moment de la compilation d'un type qui implémenteIEnumerable<T>
EnIEnumerable<T>
Lui-même.
AsEnumerable<TSource>(IEnumerable<TSource>)
peut être utilisé pour choisir entre les implémentations de requête lorsqu'une séquence implémenteIEnumerable<T>
mais dispose également d'un ensemble différent de méthodes de requête publiques disponibles. Par exemple, étant donné une classe génériqueTable
qui implémenteIEnumerable<T>
Et a ses propres méthodes telles queWhere
,Select
etSelectMany
, un appel àWhere
invoquerait la méthode publiqueWhere
deTable
. Un typeTable
qui représente une table de base de données pourrait avoir une méthodeWhere
qui prend l'argument prédicat comme une arborescence d'expression et convertit l'arborescence en SQL pour une exécution à distance. Si l'exécution à distance n'est pas souhaitée, par exemple parce que le prédicat appelle une méthode locale, la méthodeAsEnumerable<TSource>
Peut être utilisée pour masquer les méthodes personnalisées et rendre à la place les opérateurs de requête standard disponibles.
En d'autres termes.
Si j'ai un
IQueryable<X> sequence = ...;
d'un LinqProvider, comme Entity Framework, et je le fais,
sequence.Where(x => SomeUnusualPredicate(x));
cette requête sera composée et exécutée sur le serveur. Cela échouera lors de l'exécution car EntityFramework ne sait pas comment convertir SomeUnusualPredicate
en SQL.
Si je veux plutôt exécuter l'instruction avec Linq to Objects, je le fais,
sequence.AsEnumerable().Where(x => SomeUnusualPredicate(x));
maintenant le serveur renverra toutes les données et le Enumerable.Where
de Linq vers Objects sera utilisé à la place de l'implémentation du fournisseur de requête.
Peu importe qu'Entity Framework ne sache pas interpréter SomeUnusualPredicate
, ma fonction sera utilisée directement. (Cependant, cela peut être une approche inefficace car toutes les lignes seront renvoyées par le serveur.)
Je crois que AsEnumerable indique simplement au compilateur les méthodes d'extension à utiliser (dans ce cas, celles définies pour IEnumerable au lieu de celles pour IQueryable). L'exécution de la requête est toujours différée jusqu'à ce que vous appeliez ToArray ou que vous l'énumériez.