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 la relation est multiple à multiple et CompanyRolesToUsers
correspond à la table de jonction.
La plupart de nos sites sont déjà construits et la plupart des filtres fonctionnent déjà en construisant des expressions à l'aide d'une classe PredicateExtensions.
Le code pour les filtres simples ressemble à ceci:
if (!string.IsNullOrEmpty(TextBoxLastName.Text))
{
predicateAnd = predicateAnd.And(c => c.LastName.Contains(
TextBoxLastName.Text.Trim()));
}
e.Result = context.Users.Where(predicateAnd);
J'essaie d'ajouter un prédicat pour une sous-sélection dans une autre table. (CompanyRolesToUsers
)
Ce que j'aimerais pouvoir ajouter 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 facilement la procédure stockée, mais je suis novice en ce qui concerne LINQ et j'ai une date limite. Je n'ai pas été capable de trouver un exemple qui corresponde, mais je suis sûr qu'il est là quelque part.
Voici une sous-requête pour vous!
List<int> IdsToFind = new List<int>() {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.
string searchString = TextBoxLastName.Text.Trim();
predicateAnd = predicateAnd.And(c => c.LastName.Contains( searchString));
Vous voulez garder un bon contrôle sur ce qui est envoyé à la base de données. Dans le code d'origine, une lecture possible est qu'une chaîne non limitée est envoyée à la base de données pour être ajustée - ce qui n'est pas un travail satisfaisant pour la base de données.
Il n’ya pas de sous-requête nécessaire avec cette déclaration, qui est mieux écrite comme suit:
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
Encore une fois, ce sont deux manières respectables de représenter cela. Je préfère la syntaxe explicite "join" dans les deux cas moi-même, mais la voici ...
C’est comme ça que j’ai fait les sous-requêtes dans LINQ, je pense que cela devrait donner ce que vous voulez. Vous pouvez remplacer l'explicite CompanyRoleId == 2 ... par une autre sous-requête pour les différents rôles souhaités ou bien la rejoindre é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;
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 à la forum LINQtoSQL
Vous pouvez faire quelque chose comme ceci pour votre cas la syntaxe peut être un peu fausse). Regardez aussi ceci --- (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;
Voici une version du code 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)
Notez également 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 j’ai simplement codé cela dans un souci de simplicité. En réalité, il s’agit d’un tableau de valeurs CompanyRoleId, qui pourrait donc être (1) ou (2,5) ou (1,2,3,4,6,7,99).
L'autre chose que je devrais préciser plus clairement, c'est que les PredicateExtensions sont utilisées pour ajouter de manière dynamique des clauses de prédicat au Where de la requête, en fonction des champs de formulaire remplis par l'utilisateur de l'application Web. transformer la requête de travail en une expression LINQ que je peux attacher à la liste dynamique d’expressions.
Je vais donner quelques exemples de requêtes LINQ et voir si je peux les intégrer à notre code, puis je posterai mes résultats. Merci!
marcel