Ausdruckserstellungsfunktion für String.Contains und string []

.net c# expression-trees

Frage

Es fällt mir schwer, eine Expression Builder-Funktion zu erstellen, die an eine Abfrage für das NHibernate-Repository übergeben wird.

Grundsätzlich möchte ich eine Abfrage aus verschiedenen Objekteigenschaften (Strings) erstellen und deren Wert mit Werten in einem Array vergleichen. Sie können auch andere Abfragesätze hinzufügen.

Etwas wie

public static Expression<Func<TElement, bool>> BuildOrExpression<TElement, TValue>
    (Dictionary<Expression<Func<TElement, TValue>>, IEnumerable<TValue>> expressionSet)
{
    List<Expression> newExpressions = new List<Expression>();

    foreach (var item in expressionSet)
    {
        if (null == item.Key) throw new ArgumentNullException("valueSelector");
        if (null == item.Value) throw new ArgumentNullException("values");

        ParameterExpression p = item.Key.Parameters.Single();

        if (!item.Value.Any()) return e => false;

        IEnumerable<Expression> expressions = item.Value
           .Select(value => (Expression)Expression.Equal(item.Key.Body, Expression.Constant(value, typeof(TValue))));
        Expression compExpression = expressions
            .Aggregate<Expression>((accumulate, equal) => Expression.OrElse(accumulate, equal));
        newExpressions.Add(Expression.Lambda<Func<TElement, bool>>(compExpression, p));
    }

    Expression accExpression = newExpressions
        .Aggregate<Expression>((accumulate, equal) => Expression.OrElse(accumulate, equal));

    return Expression.Lambda<Func<TElement, bool>>(accExpression);
}

Aber statt die Expression.Equal irgendwie tun eine String.Contains .

Jede Hilfe wird begrüßt. Übrigens wird der obige Code nicht getestet.

Akzeptierte Antwort

Ich war in der Lage, den Ausdruck zu überwinden, der Problem kombiniert, aber ich bin nicht sicher, dass das die beste Lösung ist. Jeder, den ich die von Vladimir und PredicateBuilder von Joe Albahari (C # 4 Buchautor) zur Verfügung gestellte Lösung verwendet habe. Das ist, was mir eingefallen ist.

    public static Expression<Func<TElement, bool>> BuildOrExpression<TElement, TValue>(Expression<Func<TElement, TValue>> valueSelector, IEnumerable<TValue> values)
    {
        if (null == valueSelector) throw new ArgumentNullException("valueSelector");
        if (null == values) throw new ArgumentNullException("values");

        ParameterExpression p = valueSelector.Parameters.Single();

        if (!values.Any()) return e => false;

        Expression<Func<string, string, bool>> expFunc = (name, value) => name.Contains(value);
        IEnumerable<Expression> equals = values.Select(value => (Expression)Expression.Call(valueSelector.Body, typeof(String).GetMethod("Contains"), Expression.Constant(value, typeof(TValue))));
        Expression body = equals.Aggregate<Expression>((accumulate, equal) => Expression.OrElse(accumulate, equal));

        return Expression.Lambda<Func<TElement, bool>>(body, p);
    }
    public static Expression<Func<T, bool>> CombineOr<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
    {
        var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
        return Expression.Lambda<Func<T, bool>>(Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
    }
    public static Expression<Func<T, bool>> CombineAnd<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
    {
        var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
        return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
    }

    [TestMethod()]
    public void OfferComplexSearchTest()
    {
        using (var lifetime = container.BeginLifetimeScope())
        {
            IOfferRepository offerRepository = lifetime.Resolve<IOfferRepository>();
            List<Offer> offers = new List<Offer>();
            List<Expression<Func<Offer, bool>>> expressions = new List<Expression<Func<Offer, bool>>>();

            Expression<Func<Offer, bool>> finalExp = null;

            string[] orQuery = new string[2] { "director".ToUpper(), "jefe".ToUpper() };

            expressions.Add(LinqTools.BuildOrExpression<Offer, string>(c => c.Description.ToUpper(), orQuery));
            expressions.Add(LinqTools.BuildOrExpression<Offer, string>(c => c.Title.ToUpper(), orQuery));
            expressions.Add(LinqTools.BuildOrExpression<Offer, string>(c => c.Keywords.ToUpper(), orQuery));

            finalExp = expressions.Aggregate<Expression<Func<Offer, bool>>>((accomulate, equal) => LinqTools.CombineOr<Offer>(accomulate, equal));

            offers.AddRange(offerRepository.GetMany(LinqTools.CombineAnd<Offer>(finalExp, (x) => x.Publish)));

            Assert.IsTrue(offers.Count > 0, "Error: No Offers found.");
        }
    }

Beliebte Antwort

Anstelle von Expression.Equal Sie Expression.Call und die Methode Contains MethodInfo übergeben. Etwas wie das

IEnumerable<Expression> expressions = item.Value.Select(value => 
   (Expression)Expression.Call(item.Key.Body, 
   typeof(String).GetMethod("Contains"), 
   Expression.Constant(value, typeof(TValue))));


Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum