Ajouter dynamiquement de nouvelles expressions lambda pour créer un filtre

J'ai besoin de faire quelques filtrage sur un ObjectSet pour obtenir les entités j'ai besoin de le faire :

query = this.ObjectSet.Where(x => x.TypeId == 3); //this is just an example;

Plus tard dans le code (et avant le lancement de l'exécution différée) - je filtrer à nouveau la requête comme ceci :

query = query.Where(<another lambda here ...>);

Qui fonctionne très bien jusqu'à présent.

Voici mon problème :

Les entités contient un DateFrom de la propriété et un DateTo de la propriété, qui sont à la fois DataTime types. Ils représentent un période de temps.

J'ai besoin de filtrer les entités pour obtenir uniquement ceux qui font partie d'un collection de périodes de temps. Les périodes dans la collection ne sont pas nécessairement contigusdonc, la logique de récupérer les entités ressemble à ça :

entities.Where(x => x.DateFrom >= Period1.DateFrom and x.DateTo <= Period1.DateTo)
||
entities.Where(x => x.DateFrom >= Period2.DateFrom and x.DateTo <= Period2.DateTo)
||

... et ainsi de suite pour toutes les périodes de la collection.

J'ai essayé de faire ce que :

foreach (var ratePeriod in ratePeriods)
{
    var period = ratePeriod;

    query = query.Where(de =>
        de.Date >= period.DateFrom && de.Date <= period.DateTo);
}

Mais une fois que j'ai lancer l'exécution différée, il traduit cela en SQL tout comme je le veux (un filtre pour chacune des périodes de temps pour que de nombreuses périodes, il est dans la collection), MAIS, elle se traduit par ET des comparaisons au lieu de OU de comparaisons, qui ne retourne pas les entités à tous, depuis une entité ne peut pas faire partie de plus d'une période de temps, évidemment.

J'ai besoin de construire une sorte de dynamique linq ici pour agréger la période de filtres.


Mise à jour

Basé sur hatten réponse, j'ai ajouté les membres suivants :

private Expression<Func<T, bool>> CombineWithOr<T>(Expression<Func<T, bool>> firstExpression, Expression<Func<T, bool>> secondExpression)
{
    //Create a parameter to use for both of the expression bodies.
    var parameter = Expression.Parameter(typeof(T), "x");
    //Invoke each expression with the new parameter, and combine the expression bodies with OR.
    var resultBody = Expression.Or(Expression.Invoke(firstExpression, parameter), Expression.Invoke(secondExpression, parameter));
    //Combine the parameter with the resulting expression body to create a new lambda expression.
    return Expression.Lambda<Func<T, bool>>(resultBody, parameter);
}

Déclaré une nouvelle CombineWithOr Expression :

Expression<Func<DocumentEntry, bool>> resultExpression = n => false;

Et l'a utilisé dans ma période de collecte de l'itération comme ceci :

foreach (var ratePeriod in ratePeriods)
{
    var period = ratePeriod;
    Expression<Func<DocumentEntry, bool>> expression = de => de.Date >= period.DateFrom && de.Date <= period.DateTo;
    resultExpression = this.CombineWithOr(resultExpression, expression);
}

var documentEntries = query.Where(resultExpression.Compile()).ToList();

J'ai regardé le SQL qui en résulte, et c'est comme l'Expression n'a aucun effet. Le résultant SQL renvoie l'programmée précédemment filtres, mais pas les filtres combinés. Pourquoi ?


Mise à jour 2

Je voulais donner feO2x la suggestion de l'essayer, donc j'ai réécrit mon filtre de la requête comme ceci :

query = query.AsEnumerable()
    .Where(de => ratePeriods
        .Any(rp => rp.DateFrom <= de.Date && rp.DateTo >= de.Date))

Comme vous pouvez le voir, j'ai ajouté AsEnumerable() mais le compilateur m'a donné une erreur qu'il ne peut pas convertir l'interface IEnumerable retour à IQueryable, j'ai donc ajouté ToQueryable() à la fin de ma requête :

query = query.AsEnumerable()
    .Where(de => ratePeriods
        .Any(rp => rp.DateFrom <= de.Date && rp.DateTo >= de.Date))
            .ToQueryable();

Tout fonctionne bien. Je peux compiler le code et lancer cette requête. Toutefois, il ne convient pas à mes besoins.

Tandis que le profilage de l'résultant SQL, je peux voir que le filtrage ne fait pas partie de la requête SQL car il filtre les dates en mémoire pendant le processus. Je suppose que vous savez déjà à propos de ça et qu'est ce que vous avez l'intention de suggérer.

Votre suggestion fonctionne, MAIS, depuis qu'il extrait toutes les entités à partir de la base de données (et il y a des milliers et des milliers d'entre eux) avant de les filtrer en mémoire, il est vraiment lent à se remettre de ce montant énorme de la base de données.

Ce que je veux vraiment, c'est envoyer la période de filtrage dans le cadre de la requête SQLde sorte qu'il ne renvoie pas une énorme quantité d'entités avant de finir avec le processus de filtrage.

source d'informationauteur | 2013-04-26