J'ai une expression LINQ, qui peut être modifiée en fonction de certaines conditions. Un exemple de ce que j'aimerais faire (laissé vide le bit, je ne suis pas sûr de): Comment mettre à jour le filtre pour ajouter des paramètres supplémentaires? P> Au moment où tous les enregistrements sont récupérés, alors j'utilise un où code> pour filtrer davantage les résultats. Toutefois, cela entraîne plus de questions à la base de données que de manière strictement nécessaire. P> public static class LinqExtensionMethods
{
public static Expression<Func<T, bool>> CombineOr<T>(params Expression<Func<T, bool>>[] filters)
{
return filters.CombineOr();
}
public static Expression<Func<T, bool>> CombineOr<T>(this IEnumerable<Expression<Func<T, bool>>> filters)
{
if (!filters.Any())
{
Expression<Func<T, bool>> alwaysTrue = x => true;
return alwaysTrue;
}
Expression<Func<T, bool>> firstFilter = filters.First();
var lastFilter = firstFilter;
Expression<Func<T, bool>> result = null;
foreach (var nextFilter in filters.Skip(1))
{
var nextExpression = new ReplaceVisitor(lastFilter.Parameters[0], nextFilter.Parameters[0]).Visit(lastFilter.Body);
result = Expression.Lambda<Func<T, bool>>(Expression.OrElse(nextExpression, nextFilter.Body), nextFilter.Parameters);
lastFilter = nextFilter;
}
return result;
}
public static Expression<Func<T, bool>> CombineAnd<T>(params Expression<Func<T, bool>>[] filters)
{
return filters.CombineAnd();
}
public static Expression<Func<T, bool>> CombineAnd<T>(this IEnumerable<Expression<Func<T, bool>>> filters)
{
if (!filters.Any())
{
Expression<Func<T, bool>> alwaysTrue = x => true;
return alwaysTrue;
}
Expression<Func<T, bool>> firstFilter = filters.First();
var lastFilter = firstFilter;
Expression<Func<T, bool>> result = null;
foreach (var nextFilter in filters.Skip(1))
{
var nextExpression = new ReplaceVisitor(lastFilter.Parameters[0], nextFilter.Parameters[0]).Visit(lastFilter.Body);
result = Expression.Lambda<Func<T, bool>>(Expression.AndAlso(nextExpression, nextFilter.Body), nextFilter.Parameters);
lastFilter = nextFilter;
}
return result;
}
class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
}
5 Réponses :
Si je comprends la question, c'est probablement le problème: tout travail sur Ce dernier est composable em> et Votre autre option consiste à réécrire le filtre à combiner avant d'envoyer: p> ou réécrit de manière à permettre une utilisation pratique: p> Projets code> va utiliser énumérable code >, pas requérablement code>; Il devrait probablement être: p> . Où code> devrait fonctionner comme vous vous attendez, construisez Une requête plus restrictive
Le type de données ici n'a pas d'importance.
@Oybek la différence entre énumérable.Où code> et requisable.Lowle code> matière Très B> - pouvez-vous clarifier ce que vous dites peu importe?
Cela fonctionne, c'est juste que le projecteur.get (filtre) obtient tous les enregistrements de la base de données, puis le lieu de la base de données à nouveau. Je veux faire la requête de la base de données une seule fois. Le deuxième bit de code est comment je le fais maintenant
@Samwm Il existe une distinction importante entre ienumbertables iquéryable obtenir code> ne devrait probablement pas toucher la base de données du tout b> si elle renvoie une requête; Pouvez-vous montrer comment obtenir code> est écrit?
Lorsque vous récupérez à l'aide de la méthode code> code> ici, il renvoie un certain objet. iEnumerable Iquerible où code> comme il se doit. S'il est requis, il produira une arborescence d'expression prologie dans tous les cas.
@Oybek non, pas vrai; Le type de la variable B> (pas l'objet) est crucial ici; S'il est seulement connu d'être ienumerable iquéryable énumérable. code>, qui est linq-to-objets. Il ne "compose" pas la requête. Désolé, mais ce que vous dites est faux.
@Samwm j'ai ajouté une deuxième approche, de réécriture d'expression-arborescence; Vous pouvez utiliser cela pour combiner deux filtres avant B> appeler Get - Toute utilisation?
@Samwm oui, mais il faudrait devoir être filtre = filtre.combine (x => x.b> 2.5); code> etc - c'est-à-dire une valeur de retour que vous attrapez. Et vous voudrez peut-être appeler cela andalso code> ou quelque chose
@Samwm en fait, il est probablement préférable d'utiliser andalso code> dans l'expression elle-même (éditera cela)
Si vous Get CODE> La méthode Retroie les données et renvoie dans des objets de mémoire, vous pouvez le faire Expression<Func<Project, bool>> filter = (Project p) => p.UserName == "Bob";
if(showArchived) {
filter = (Project p) => p.UserName == "Bob" && p.Archived;
}
IEnumerable<Project> projects = unitOfWork.ProjectRepository.Get(filter);
Qui ajoute du code supplémentaire redondant. Le filtre initial peut être plus que de vérifier le nom d'utilisateur.
Tout cela dépend de la façon dont Mais si la méthode projecteur.get () code> se comporter et ce qu'il revient. La voie habituelle (par exemple, Linq à SQL fait quelque chose comme ça) est qu'il renvoie un iquéryable où () code > Clauses Avant de l'envoyer au serveur sous la forme d'une requête SQL, avec tous les où () code> clauses incluses. Si tel est le cas, la solution de Mark (utilisez iquerybale get () code> exécute la requête en fonction du filtre code> immédiatement, vous devez le transmettre tout le filtre dans l'expression. Pour ce faire, vous pouvez utiliser prédicatbuilder code> . P >
se débarrasser de tolist () code> et tout ira bien. P>
Je pense que vous voulez combiner des filtres de cette façon:
public static Expression<Func<T, bool>> OrTheseFiltersTogether<T>(params Expression<Func<T, bool>>[] filters)
{
return filters.OrTheseFiltersTogether();
}
public static Expression<Func<T, bool>> OrTheseFiltersTogether<T>(this IEnumerable<Expression<Func<T, bool>>> filters)
{
if (!filters.Any())
{
Expression<Func<T, bool>> alwaysTrue = x => true;
return alwaysTrue;
}
Expression<Func<T, bool>> firstFilter = filters.First();
var body = firstFilter.Body;
var param = firstFilter.Parameters.ToArray();
foreach (var nextFilter in filters.Skip(1))
{
var nextBody = Expression.Invoke(nextFilter, param);
body = Expression.OrElse(body, nextBody);
}
Expression<Func<T, bool>> result = Expression.Lambda<Func<T, bool>>(body, param);
return result;
}
public static Expression<Func<T, bool>> AndTheseFiltersTogether<T>(params Expression<Func<T, bool>>[] filters)
{
return filters.AndTheseFiltersTogether();
}
public static Expression<Func<T, bool>> AndTheseFiltersTogether<T>(this IEnumerable<Expression<Func<T, bool>>> filters)
{
if (!filters.Any())
{
Expression<Func<T, bool>> alwaysTrue = x => true;
return alwaysTrue;
}
Expression<Func<T, bool>> firstFilter = filters.First();
var body = firstFilter.Body;
var param = firstFilter.Parameters.ToArray();
foreach (var nextFilter in filters.Skip(1))
{
var nextBody = Expression.Invoke(nextFilter, param);
body = Expression.AndAlso(body, nextBody);
}
Expression<Func<T, bool>> result = Expression.Lambda<Func<T, bool>>(body, param);
return result;
}
Cette approche n'est pas faux i>, mais est mal soutenue par de nombreux moteurs LINQ; LINQ-TO-SQL convient parfaitement à l'expression.Invoke, cependant, EF ne l'aime pas. En tant que tel, il est plus fiable (et pas de travail supplémentaire) d'utiliser l'approche "visiteur" pour combiner les prédicats directement b>.
Nous semblions prometteurs, veulent être aussi génériques que possible, car il existe de nombreuses classes différentes impliquées. Utiliser un cadre d'entité, mais dans l'espoir de la preuve future si quelque chose d'autre serait jamais utilisé (comme NHibernate). Comme l'idée de construire une liste de filtres, il suffit de combiner avant d'exécuter
@Samwm Idéalement, vous devriez être capable de combiner mes signatures de méthode simples avec les visiteurs d'expression de Marc pour vous rendre à une solution EF utilisée.
Quel est le type de retour et les interlans de
projecteur.get (filtre); code>?Qu'est-ce que
showached code>? Est-ce qu'il énumèrevariables code>?ShowRarchived est juste un booléen
Maintenant que vous avez ajouté obtenir, la Tolist () est une douleur; J'ai ajouté un exemple d'expression-réécriture que vous devriez pouvoir utiliser pour combiner les deux filtres avant b> appeler