Alors voilà, je (re)commence à jouer avec Linq et Linq to SQL en ce moment.
J’ai une petite question :
Je souhaiterai faire une requête récursive. Voilà, j’ai une table (T_ITEMS) avec une relation récursive :
[codebox]T_ITEMS
ItemID
ParentItemID
TitleItem[/codebox]
Or je ne vois pas très bien comment faire cette requête avec Linq. J’ai commencé à chercher et j’ai trouvé ça
Malheureusement je ne comprends pas très bien et le code associé ne m’aide pas vraiment. Je me suis dis que d’autres étaient peut-être dans le même cas que moi alors…
Ce que j’ai compris c’est qu’on peut grouper les données projetées en fonction d’un élément :
[codebox]var query = from i in Items
group i by i.ParentRubriqueID into g
select g[/codebox]
En revanche pour récupérer ces données de façon typées , je suis un peu paumé.
Sql server 2005 Express Edition mais je pense que ça n’a pas d’importance puisqu’en fait c’est la “requête” Linq que je cherche et c’est le moteur qui va la transformer en requête SQL.
Si t’as juste un niveau de profondeur ou un truc sans branches, ok, mais sinon si c’est tout une arborescence, je pense que c’est pas la maniere optimale de stocker un arbre dans une SGBD surtout
Si tu as utilisé l’outils de génération de classes Linq To SQL, il a du te générer des propriétés ParentCategory et ChildrenCategories (le nom des propriétés n’est pas garanti ^^), permettant d’accéder aux entitées reliées et poser des contraintes dessus.
Par défaut, les entitées reliées sont chargées en mode “Lazy Loading”, c’est à dire qu’une requète est effectuée au moment ou l’on veut y accéder. Cependant, on peut modifier ce comportement grâce aux “DataLoadOptions” du DataContext.
De mémoire, le code doit ressembler plus ou moins à ca:
var options = new DataLoadOptions();
options.LoadWith((Category cat)=>cat.ChildrenCategories);
myContext.DataLoadOptions = options;
Il doit y avoir aussi un moyen, dans le cas où le LoadWith est récursif, d’indiquer le nombre maximal de récursions à effectuer.
ensuite, tu vas pouvoir faire:
var rootCategories = from cat in ctx.Categories
where cat.ParentCategory == null
select cat;
Et voilà, tu as tes catégories racines, avec les “ChildrenCategories” préchargées.
Une autre précision : je voulais ‹ isoler › cette méthode dans la couche d’accès aux données de mon appli. Pour ce faire, j’ai rajouté une classe partielle dans mon datacontext (ça sonne faut mais vous comprendrez l’idée) dans laquelle j’ai rajouté ma méthode.
1°) Est-ce que ça se fait / est-ce la bonne manière ?
2°) C’est pour ça que j’ai quelques petits soucis (je ne peux pas utiliser de types anonymes par exemple).
Je reviens vers vous dès que j’aurai avancé sur le sujet.
Ca se fait et c’est une relativement bonne chose, car la génération des requêtes SQL est le rôle de la couche d’accès aux données. Moins tes IQueryable sont visibles des couches supérieures, et mieux tu te portes.
Maintenant, le soucis, c’est que forcément, si tu étends le DataContext par partialité, ca veut dire que ta couche du dessus doit connaitre ta classe DataContext, donc peut utiliser directement les “IQueryable”.
et en fait il me jette une belle exception au runtime :
System.InvalidOperationException was unhandled
Message="Cycles non autorisés dans le graphique de type LoadOptions LoadWith."
System.Data.Linq.DataLoadOptions.ValidateTypeGraphAcyclic()
Apparement il ne gère pas la récursivité…
PS : Le nom des propriétés générées est assez bizarre, pour le parent il met [nom_entité]1 et pour les enfants [nom_entité]s (sans les crochets). Elles sont évidemment modifiables.
Dans ce cas tu peux faire un truc du genre:
from i in ctx.Items
where i.Parent == null
select new { Item = i, Children = from i2 in i.Children
select new{Item = i2, Children= i2.Children};