Comment faire une sous-requête dans LINQ?

Voici un exemple de requête que je tente de convertir en LINQ:

SELECT * FROM Users WHERE Users.lastname LIKE '%fra%' AND Users.Id IN ( SELECT UserId FROM CompanyRolesToUsers WHERE CompanyRoleId in (2,3,4) ) 

Il existe une relation FK entre CompanyRolesToUsers et Users , mais il s’agit d’une relation plusieurs à plusieurs et CompanyRolesToUsers est la table de jonction.

Nous avons déjà construit la majeure partie de notre site et nous avons déjà la majeure partie du filtrage en construisant des expressions utilisant une classe PredicateExtensions.

Le code pour les filtres simples ressemble à ceci:

  if (!ssortingng.IsNullOrEmpty(TextBoxLastName.Text)) { predicateAnd = predicateAnd.And(c => c.LastName.Contains( TextBoxLastName.Text.Trim())); } e.Result = context.Users.Where(predicateAnd); 

J’essaie d’append un prédicat pour une sous-sélection dans une autre table. ( CompanyRolesToUsers )

Ce que je voudrais pouvoir append est quelque chose qui fait ceci:

 int[] selectedRoles = GetSelectedRoles(); if( selectedRoles.Length > 0 ) { //somehow only select the userid from here ???: var subquery = from u in CompanyRolesToUsers where u.RoleID in selectedRoles select u.UserId; //somehow transform this into an Expression ???: var subExpression = Expression.Invoke(subquery); //and add it on to the existing expressions ???: predicateAnd = predicateAnd.And(subExpression); } 

Est-ce qu’il y a un moyen de faire ça? C’est frustrant parce que je peux écrire la procédure stockée facilement, mais je suis nouveau dans ce truc LINQ et j’ai une date limite. Je n’ai pas été capable de trouver un exemple qui correspond, mais je suis sûr que c’est quelque part.

Voici une sous-question pour vous!

 List IdsToFind = new List() {2, 3, 4}; db.Users .Where(u => SqlMethods.Like(u.LastName, "%fra%")) .Where(u => db.CompanyRolesToUsers .Where(crtu => IdsToFind.Contains(crtu.CompanyRoleId)) .Select(crtu => crtu.UserId) .Contains(u.Id) ) 

En ce qui concerne cette partie de la question:

 predicateAnd = predicateAnd.And(c => c.LastName.Contains( TextBoxLastName.Text.Trim())); 

Je recommande fortement d’extraire la chaîne de la zone de texte avant de créer la requête.

 ssortingng searchSsortingng = TextBoxLastName.Text.Trim(); predicateAnd = predicateAnd.And(c => c.LastName.Contains( searchSsortingng)); 

Vous voulez garder un bon contrôle sur ce qui est envoyé à la firebase database. Dans le code d’origine, une lecture possible est qu’une chaîne non limitée est envoyée dans la firebase database pour le rognage – ce qui n’est pas un bon travail pour la firebase database.

Il n’y a pas de sous-requête nécessaire avec cette instruction, qui est mieux écrite comme

 select u.* from Users u, CompanyRolesToUsers c where u.Id = c.UserId --join just specified here, perfectly fine and u.lastname like '%fra%' and c.CompanyRoleId in (2,3,4) 

ou

 select u.* from Users u inner join CompanyRolesToUsers c on u.Id = c.UserId --explicit "join" statement, no diff from above, just preference where u.lastname like '%fra%' and c.CompanyRoleId in (2,3,4) 

Cela étant dit, dans LINQ, ce serait

 from u in Users from c in CompanyRolesToUsers where u.Id == c.UserId && u.LastName.Contains("fra") && selectedRoles.Contains(c.CompanyRoleId) select u 

ou

 from u in Users join c in CompanyRolesToUsers on u.Id equals c.UserId where u.LastName.Contains("fra") && selectedRoles.Contains(c.CompanyRoleId) select u 

Ce qui, encore une fois, sont les deux manières respectables de représenter cela. Je préfère la syntaxe explicite “join” dans les deux cas, mais voilà …

C’est comme ça que je fais des sous-requêtes dans LINQ, je pense que cela devrait vous donner ce que vous voulez. Vous pouvez remplacer explicitement CompanyRoleId == 2 … par une autre sous-requête pour les différents rôles souhaités ou la joindre également.

 from u in Users join c in ( from crt in CompanyRolesToUsers where CompanyRoleId == 2 || CompanyRoleId == 3 || CompanyRoleId == 4) on u.UserId equals c.UserId where u.lastname.Contains("fra") select u; 

Vous pourriez faire quelque chose comme ça pour votre cas – (la syntaxe est peut-être un peu faible). Regardez aussi ce lien

 subQuery = (from crtu in CompanyRolesToUsers where crtu.RoleId==2 || crtu.RoleId==3 select crtu.UserId).ToArrayList(); finalQuery = from u in Users where u.LastName.Contains('fra') && subQuery.Contains(u.Id) select u; 

Ok, voici une requête de jointure de base qui obtient les enregistrements corrects:

  int[] selectedRolesArr = GetSelectedRoles(); if( selectedRolesArr != null && selectedRolesArr.Length > 0 ) { //this join version requires the use of distinct to prevent muliple records //being returned for users with more than one company role. IQueryable retVal = (from u in context.Users join c in context.CompanyRolesToUsers on u.Id equals c.UserId where u.LastName.Contains( "fra" ) && selectedRolesArr.Contains( c.CompanyRoleId ) select u).Distinct(); } 

Mais voici le code qui s’intègre le plus facilement à l’algorithme que nous avions déjà en place:

 int[] selectedRolesArr = GetSelectedRoles(); if ( useAnd ) { predicateAnd = predicateAnd.And( u => (from c in context.CompanyRolesToUsers where selectedRolesArr.Contains(c.CompanyRoleId) select c.UserId).Contains(u.Id)); } else { predicateOr = predicateOr.Or( u => (from c in context.CompanyRolesToUsers where selectedRolesArr.Contains(c.CompanyRoleId) select c.UserId).Contains(u.Id) ); } 

qui est grâce à une affiche sur le forum LINQtoSQL

Voici une version du SQL qui renvoie les enregistrements corrects:

 select distinct u.* from Users u, CompanyRolesToUsers c where u.Id = c.UserId --join just specified here, perfectly fine and u.firstname like '%amy%' and c.CompanyRoleId in (2,3,4) 

De plus, notez que (2,3,4) est une liste sélectionnée par l’utilisateur de l’application Web dans une liste de cases à cocher et j’ai oublié de mentionner que je viens de coder cela pour plus de simplicité. C’est vraiment un tableau de valeurs CompanyRoleId, il pourrait donc s’agir de (1) ou (2,5) ou (1,2,3,4,6,7,99).

L’autre chose que je devrais préciser, c’est que les PredicateExtensions sont utilisées pour append dynamicment des clauses de prédicats à la requête Where, en fonction des champs de formulaire que l’utilisateur de l’application Web a remplis. transformer la requête de travail en expression LINQ que je peux joindre à la liste dynamic des expressions.

Je vais donner quelques exemples de requêtes LINQ et voir si je peux les intégrer à notre code, puis obtenir mes résultats. Merci!

marcel