[new:31/01/2011]WCF Ria Services est une mécanique de précision. En démo tout est toujours simple et évident, dans la réalité on rencontre toujours quelques cas plus retors et pas forcément dans des features ultra sophistiquées... Par exemple le chargement d’entités associées réclame de connaitre la double astuce que je vais vous présenter.
Des entités et des associations
De base, WCF Ria Services permet de publier des données relationnelles provenant d’un modèle Entity Framework. Aujourd’hui on peut aussi s’en servir avec des POCO (Plain Old CLR Object) mais restons dans le cadre de EF.
Imaginons une entité Customer, une classe associée à l’entité Activity. Cette dernière représente une activité enregistrée dans le dossier client, une sorte d’historique. Un client peut avoir de nombreuses activités, il s’agit, d’une association 1,1->0,n c’est à dire qu’un client peut avoir zéro ou n activités enregistrées alors qu’une activité est associée à un et un seul client de façon systématique.
Avec Entity Framework tout cela se fait automatiquement par reverse engineering de la base de données lorsqu’on créé le modèle.
Une telle relation (qu’on qualifie aussi de type “maitre /détail”) est traversable dans les deux sens. Depuis l’entité Customer, EF met à notre disposition une propriété Activities (ou un autre nom choisi par le développeur) qui est une collection d’entités Activity. Depuis une entité Activity EF ajoute aussi une propriété de navigation “Customer” qui pointe vers l’entité client maitre.
Nous sommes vraiment là dans un cas rudimentaire ultra classique, ne cherchez pas où se trouve l’astuce pour le moment, on est dans le basique de chez basique !
Une fois le décor planté, passons aux choses sérieuses.
Interrogation des données avec les entités associées
Si j’interroge les clients (avec une requête LINQ depuis Silverlight par exemple) j’obtiendrais des entités clients. Chaque fiche contenant un propriété de navigation vers ses activités je m’attends à pouvoir travailler sur cette liste. Il n’en est rien. C’est l’aspect Lazy Loading des RIA Services.
Le Lazy Loading, littéralement “chargement paresseux”, consiste à ne pas renvoyer toute la grappe des entités liées. C’est heureux car un système qui ne fonctionnerait pas comme cela serait inutilisable. Imaginez un instant que j’interroge une fiche client et que, automatiquement, le système me remonte l’ensemble de ses devis, factures, avoirs, avec chacun leur ligne article qui chacune renverrait la fiche fournisseur, etc... La requête d’une seule entité retournerait presque toute la base de données !
On doit pouvoir maitriser le chargement des entités associées pour éviter de tomber dans ce travers rédhibitoire. C’est ce que fait le Lazy Loading des Ria Services.
De même, en reprenant mon exemple Client->Activité, si j’interroge les activités j’aurais bien dans chacune une propriété de navigation “Customer”, mais celle-ci sera “null” par défaut.
Prenons la requête suivante :
var q = from a in Activities where !a.HasBeenRead return a;
Il s’agit ici de remonter toutes les entités de type Activity dont la propriété HasBeenRead (“a été lue”) est à False. De base il me sera impossible d’accéder à la ficher client de chaque entité Activity, bien que la propriété existe sa valeur sera nulle.
Autoriser le chargement des entités associées
La première chose à faire est d’indiquer lors de la requête qu’on souhaite charger les entités Customer associées à chaque entités Activity. La requête devient alors :
var a = from a in Activities.Include("Customer") where ...
“Customer” est la propriété pointant vers la fiche client de l’Activité. En ajoutant Include(“propriété”) nous demandons à LINQ et aux Ria Services d’inclure la fiche associée dans la grappe de données qui sera remontée.
On pourrait croire que cela est suffisant. Hélas non. La propriété Customer sera toujours retournée à null...
Pour que cela fonctionne il faut _aussi_ marquer de l’attribut Include la propriété Customer dans les métadonnées du modèle EF (dans la classe ActivityMetadata)...
internal sealed class ActivityMetadata
{
...
[Include]
public Customer Customer { get; set; }
}
Et voilà... C’est tout bête mais il faut le savoir.
L’attribut Include dans les métadonnées autorise le chargement des entités associées, ce qui permet dans les requêtes LINQ d’utiliser la méthode Include lorsqu’on désire obtenir ces fameuses entités associées. Utiliser uniquement la syntaxe LINQ n’est pas suffisant si le chargement n’a pas été autorisé dans les métadonnées...
Conclusion
Rien de bien sorcier dans ce billet mais si on ne connait pas l’astuce on peut rester coincer un petit moment à se demander pourquoi les propriétés de navigation retournées par les requêtes restent obstinément à null alors même qu’on ajoute le Include dans les requêtes !
Stay Tuned !