LINQ To Entities vs LINQ To SQL
Je reviendrai plus tard sur l'Entity Framework (EF) et l'Entity Data Model.
Dans ce post, je voulais juste illustrer les modifications sur le requêtage LINQ entre LINQ To Entities et LINQ To SQL. En effet, contre toute attente (du moins de mon point de vue), il existe des différences dans le requêtage LINQ.
Prenons la base Northwind on veut récupérer tous les produits regroupés par catégories. La manière la plus simple de faire cela est tout bêtement d'utiliser la relation qui existe entre Categories et Products.
Avec LINQ To SQL, cela se traduit par :
foreach (var c in context.Categories)
{
Console.WriteLine(c.CategoryName);
foreach (var p in c.Products)
Console.WriteLine(" " + p.ProductName);
}
Avec LINQ To Entities, cette même requête pose deux problèmes :
Tout d'abord, et je ne me l'explique pas du tout, p n'est pas reconnu comme une instance de Products et du coup, le code ne compile pas.
Pourtant la propriété Products de Categories est de type System.Data.Objects.DataClasses.EntityCollection<Products> et EntityCollection<TEntity> : IEnumerable<TEntity>.
Mon réflexe a donc été de ne pas utiliser le deuxième var :
foreach (var c in context.Categories)
{
Console.WriteLine(c.CategoryName);
foreach (Products p in c.Products)
Console.WriteLine(" " + p.ProductName);
}
Ce code compile mais là, surprise à l'exécution, il n'y a aucun produit !
En effet, avec LINQ To Entities, les entités liées par une relation ne sont pas récupérées (par défaut). Il faut donc le préciser avec la méthode Include :
foreach (var c in context.Categories.Include("Products"))
{
Console.WriteLine(c.CategoryName);
foreach (Products p in c.Products)
Console.WriteLine(" " + p.ProductName);
}
Soit dit en passant, je trouve que ce n'est pas super de passer Products en chaine de caractères. Du coup, le compilateur ne peut pas vérifier les erreurs. Sauf si vous utilisez ceci. Attention ExpressionType.Cast n'existe plus. Pour ma part, je l'ai réécris comme ça :
public static PropertyInfo GetProperty(Expression<Func<T, object>> selector)
{
if (selector == null)
throw new ArgumentNullException();
Expression e = selector.Body;
while (!((e is MemberExpression) && (e.NodeType == ExpressionType.MemberAccess) && ((e as MemberExpression).Member is PropertyInfo)))
{
if (e is UnaryExpression)
{
switch (e.NodeType)
{
case ExpressionType.Convert:
case ExpressionType.ConvertChecked:
case ExpressionType.TypeAs:
e = (e as UnaryExpression).Operand;
break;
default:
throw new ArgumentException();
}
}
else
throw new ArgumentException();
}
return (e as MemberExpression).Member as PropertyInfo;
}
Du coup le Include peut alors s'écrire comme ceci :
context.Categories.Include(ReflectionHelper<Categories>.GetProperty(c => c.Products).Name)
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :