Je tiens vivement à remercier Flavien pour m'avoir notifié une ligne de code inutile. En ont découlé des discussions très instructives avec Flavien CHARLON et Matthieu MEZIL qui m'ont amené à retester différents cas de figure et à montrer que la surcharge du « Select » pour « Linq To SQL » n'était pas une solution satisfaisante.
Rappelons le code proposé :
internal static class ExtensionLinq
{
internal static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source,
Func<TSource, TResult> selector)
{
IEnumerable<TSource> sourceEnum = source;
foreach (TSource s in sourceEnum)
yield return selector.Invoke(s);
}
}
la ligne « IEnumerable<TSource> sourceEnum = source; » est complètement inutile car le « yield return » fabrique sous le capot un objet privé implémentant IEnumerable<T> et non IQueryable<T>. Voici le code que j'aurai dû écrire :
internal static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source,
Func<TSource, TResult> selector)
{
foreach (TSource s in source)
yield return selector.Invoke(s);
}
On pourrait être satisfait mais le Select ne fonctionne pas comme on le souhaiterait. En effet, si au lieu de renvoyer toutes les colonnes de la table Personne, nous sélectionnons uniquement le nom par exemple :
var c = from h in dt.PERSONNEs where h.Personne_Age > 30 select h.Personne_Nom;
La requête effectuée vers la base de donnée renverra toutes les colonnes avant que n'opère le « Select » que nous avons écrit. Cette solution n'est donc pas satisfaisante. Si l'on souhaite que la base de données ne renvoie que les colonnes que l'on a réellement besoin, il faut faire appel à la méthode d'extension « Select » de « System.Linq.Queryable » :
internal static IEnumerable<TResult> Select<TSource, TResult>(this IQueryable<TSource> source,
System.Linq.Expressions.Expression<Func<TSource, TResult>> selector)
{
IQueryable iq = Queryable.Select(source, selector);
foreach (TResult s in iq)
yield return s;
}
Si vous utilisez le Debugger ou le système de log du « DataContext », vous pourrez constater que cette surcharge répond désormais à notre exigence.
Toutefois, les discutions avec mes camarades m'ont amené à effectuer de nombreux tests. J'ai alors pu constater que cette surcharge ne fonctionnait pas dans certains cas. Par exemple, si vous écrivez une requête du style « select * from matable where condition », le compilateur ne génèrera que « Table<matable>.Where(condition) » car le « Select » est inutile car toutes les colonnes sont demandées. On peut donc se féliciter d'un tel comportement pour ce qui est de la performance. Mais, la surcharge que je vous propose, ne fonctionne donc pas dans tous les cas. Je vous recommande donc de ne pas l'employer dans votre couche d'accès. Vous pouvez écrire une méthode d'extension qui empêchera de pouvoir requêter de nouveau via « Linq to SQL » :
internal static IEnumerable<TSource> ToLinqToObject<TSource>(this IQueryable<TSource> source)
{
foreach (TSource s in source)
yield return s;
}
Son utilisation se fera comme suit :
public IEnumerable<string> GetPersonnes()
{
DataClasses1DataContext t = new DataClasses1DataContext(myDatabaseConnectionString);
var c = from h in t.PERSONNEs where h.Personne_Age > 30 select h;
return c.ToLinqToObject();//return from i in c.AsEnumerable() select i;
}
Voici les liens des différentes discussions avec Flavien et Matthieu :