Dot.Blog

C#, XAML, Xamarin, UWP/Android/iOS

Linq to Entities : “.Date” non supporté – Une solution

[new:5/04/2011]Quand on utilise Wcf Ria Services on utilise souvent côté serveur Linq To Entities. Et quand on utilise Linq on aime bien tester ses requêtes avec l’outil LinqPad.  Voici un ménage à trois qui nous réserve parfois des surprises, des bonnes et des moins bonnes...

Séparons bien les problèmes

Le problème principal est que Linq To Entities ne supporte pas le “.Date” des DateTime.

Le fait d’utiliser Linq To Entities avec les Wcf Ria Services (avec Silverlight) n’est qu’un cas d’utilisation parmi d’autres. On peut utiliser L2E même dans une application Console ou dans une application WCF. Il s’agit juste d’une complication qui rend le debug plus délicat.

Quant à LinqPad, c’est un outil dont je vous ai certainement déjà parlé tellement il est pratique. Et si vous ne connaissez pas, suivez le lien et téléchargez (c’est gratuit) et vous me remercierez plus tard...

Mais voilà, LinqPad utilise Linq To SQL pour se connecter aux données. Linq To SQL est en quelque sorte l’ancêtre de Linq To Entities et on s’attend à voir passer dans ce dernier ce qui passe aussi dans le premier. Or, il faut comprendre que Linq To SQL a été écrit en adéquation avec SQL Server, alors que Linq To Entities est conçu pour supporter différents serveurs (ce qui en fait, en partie, l’avantage).

Quand on travaille sur une application utilisant Linq to Entities, on ne va pas se priver d’utiliser LinqPad pour tester ses requêtes, cela serait idiot. Mais parfois on oublie que ce n’est pas du Linq To Entities. Dès lors, certaines requêtes bien parfumées et épilées de près qui tournent comme une ballerine sur le Casse Noisette se plantent lamentablement tel un volatile malchanceux jouant le final du Lac des Cygnes. Et tout cela uniquement devant la salle pleine et non pendant les répétitions – je veux dire au runtime et non à la compilation.

Un avertissement pour commencer

Ecrire cent fois :

“je ne prendrais pas pour argent comptant les tests de requêtes effectués sous LinqPad lorsque je travaille sous Linq To Entities”.

C’est un moyen un peu vieillot et totalement idiot mais redoutablement efficace pour retenir une leçon...

Revenons au problème

Bref, il y a un problème, L2E ne supporte pas “.Date” alors que Linq to SQL le supporte.

Vous allez me dire, on s’en fiche un peu non ?

Non, pas du tout car dans des applis de gestion (des LOB’s disent en ce moment les américains - et les français qui aiment faire les malins à la machine à café) il n’est pas rare du tout de faire des choses comme un groupage de données sur la date...

Et un groupage sur la date veut dire sur la date, non pas sur la date et l’heure (type DateTime). Les groupes ne veulent plus rien dire lorsqu’on prend en compte la partie heure au millième de seconde !

Il faudrait pouvoir extraire la partie Date uniquement, et c’est justement ce que fait “.Date” de DateTime.

Du coup, une requête comme celle-ci ne passe pas sous L2E :

var q = from x in MaBase.MaTable
        group x by x.DateDeFacturation.Date into g
        select g;
 

Dommage...

Certains s’en sortiront très vite en ajoutant une colonne calculée à la base de données pour isoler la partie Date. Ce n’est pas forcément une mauvaise solution mais elle oblige a modifier le schéma de la base ce qui, dans de nombreuses situations, ne se fait pas comme ça tout seul dans son coin et uniquement pour simplifier l’écriture d’une requête.

La solution

Heureusement on peut aussi être filou et réfléchir un peu. Et parfois on trouve une solution qui à défaut d’être d’une grande élégance, reste simple et facile à implémenter.

Dans notre cas l’astuce consiste à créer une variable locale dans la requête et à grouper sur un type anonyme (solution souvent intéressante dans d’autres cas, comme le groupage sur plusieurs champs, normalement impossible).

La requête précédente devient alors :

var q = from x in MaBase.MaTable
        Let d = x.DateDeFacturation // la variable locale
        group x by new {y = d.Year, m = d.Month, d = d.Day} into g // le type anonyme
        select g;

 

Chat échaudé...

... craint l’eau froide. C’est bête mais je me rappelle que quand j’étais petit je ne comprenais pas ce proverbe. Je ne voyais pas en quoi un pauvre chat qui a eu trop chaud pouvait craindre  l’eau froide alors même que, au contraire, d’avoir eu trop chaud devait l’inciter à chercher la fraicheur bienfaisante d’une eau froide... J’étais trop logique, et je cherchais trop la logique partout, déjà petit j’étais informaticien... Sourire

Passons l’anecdote et rappelons qu’en bon chat échaudé par le “.Date” nous allons nous méfier de l’eau froide qui coule dans toutes les notions de temps. Et nous avons raison ! Car de nombreux soucis existent aussi avec les TimeStamp sous L2E. Mais je vous laisse la joie de le découvrir... (je vous aurais tout de même prévenu !).

On en revient au fait de bien tester ses requêtes à l’exécution, et donc à la tentation d’utiliser LinqPad, et à l’avertissement donné plus haut ... Tout se tient Sourire

Conclusion

Ici nous nous en sortons plutôt bien, la pirouette est possible. Dans d’autres cas L2E est parfois moins souple et il faut repenser les requêtes autrement, voir travailler en plusieurs passes avec du Linq To Object coincé comme une tranche de jambon entre deux tranches de Linq To Entities. Drôle de sandwich, surtout que souvent, il n’y manque rien puisque nous y jouons le rôle du cornichon...

Sur ce, Stay Tuned !

blog comments powered by Disqus