Curry-Ausdrücke in C #

c# currying expression-trees linq tsql

Frage

Ich versuche, einen Ausdrucksbaum zu erstellen, den ich in Linq2SQL einspeisen kann, so dass es eine nette saubere Abfrage erzeugen wird. Mein Zweck ist es, einen Filter zu erstellen, der eine beliebige Menge von Wörtern zu UND und NICHT (oder ODER und NICHT) zusammennimmt. Da ich die Felder, nach denen ich suche, variieren möchte, möchte ich vorzugsweise eine Liste von Expresssion<Func<T, string, bool>> zusammenstellen (wobei T die Entität ist, auf der ich Expresssion<Func<T, string, bool>> ), indem ich eine Vielzahl von Expresssion<Func<T, string, bool>> Funktionen. Dann würde ich eine Reihe von Wörtern erhalten und diese Expresssion<Func<T, bool>> und eine Expresssion<Func<T, bool>> up (die bestimmte Ausdrücke negieren würde) erstellen, die ich schließlich einer .Where-Anweisung zuführen kann.

Ich habe LINQKit PredicateBuilder verwendet, aber dieser Code befasst sich mit einzelnen Parameterausdrücken. Es hat mir jedoch einige Grundlagen für meine eigenen Versuche gegeben. Ich möchte etwas in der Art machen:

var e = (Expression<Func<Entity, string, bool>>)((p, w) => p.SomeField.ToLower().Contains(w));

var words = new []{"amanda", "bob"};

var expr = (Expression<Func<Entity, bool>>)(p => false);
// building up an OR query
foreach(var w in words) {
    var w1 = w;
>>>>expr = Expression.Lambda<Func<Entity, bool>>(Expression.OrElse(expr.Body, (Expression<Func<Entity, bool>>)(p => e(p, w))));
}

var filteredEntities = table.Where(expr);

Aber da ich Ausdrücke verwende, ist die mit >>>> gekennzeichnete Zeile offensichtlich illegal (kann nicht e(p, w) wie ich für eine Funktion tun). Meine Frage ist also, wie kann ich eine einzelne Variable (das Wort) partiell auf Ausdrücke anwenden, die Funktionen mit mehreren Parametern enthalten?


Okay, hab in LINQPad herumgetüftelt und eine Lösung gefunden, die für mich funktioniert. Diese Frage hat mich dorthin gebracht. Ich bin ziemlich neu beim Aufbau von Expression-Bäumen, daher würde ich Kommentare und Antworten mit Verbesserungen oder Kritik schätzen (und upvoten).

// Some set of expressions to test against
var expressions = new List<Expression<Func<Entity, string, bool>>>();
expressions.Add((p, w) => p.FirstName.ToLower().Contains(w));
expressions.Add((p, w) => p.LastName.ToLower().Contains(w));
expressions.Add((p, w) => p.Department != null && p.Department.Name.ToLower().Contains(w));

var words = new []{"amanda", "bob"};
var negs = new []{"smith"}; // exclude any entries including these words

var isAndQuery = true; // negate for an OR query
Expression<Func<Entity, bool>> posExpr = p => isAndQuery;

var entityParameter = Expression.Parameter(typeof(Entity), null);

// Build up the NOTs
var negExpr = (Expression<Func<Entity, bool>>)(p => true);
foreach(var w in negs) {
    var w1 = w;
    foreach(var e in expressions) {
        var andNot = Expression.Invoke(e, entityParameter, Expression.Constant(w1));
        negExpr = Expression.Lambda<Func<Entity, bool>>(Expression.AndAlso(negExpr.Body, Expression.Not(andNot)), entityParameter);
    }
}

// Build up the ANDs or ORs
foreach(var w in words) {
    var w1 = w;
    var orExpr = (Expression<Func<Entity, bool>>)(p => false);
    foreach(var e in expressions) {
        var orElse = Expression.Invoke(e, entityParameter, Expression.Constant(w1));
        orExpr = Expression.Lambda<Func<Entity, bool>>(Expression.OrElse(orExpr.Body, orElse), entityParameter);
    }
    var orInvoked = Expression.Invoke(orExpr, posExpr.Parameters.Cast<Expression>());
    if(isAndQuery)
        posExpr = Expression.Lambda<Func<Entity, bool>>(Expression.AndAlso(posExpr.Body, orInvoked), entityParameter);
    else
        posExpr = Expression.Lambda<Func<Entity, bool>>(Expression.OrElse(posExpr.Body, orInvoked), entityParameter);
}
var posInvoked = Expression.Invoke(posExpr, posExpr.Parameters.Cast<Expression>());
var finalExpr = Expression.Lambda<Func<Entity, bool>>(Expression.AndAlso(negExpr.Body, posInvoked), entityParameter);

var filteredEntities = entities.Where(finalExpr);

Beliebte Antwort

Ich benutze linq um epession Bäume zu bauen, es macht mich sehr leistungsfähig, also habe ich das hinzugefügt, nicht als eine vollständige Antwort auf deine Frage, aber mehr als eine elegante Art, Ausdrucksbäume aufzubauen ...

var query = ...;
var search = "asdfasdf";
var fields = new Expression<Func<MyEntity,string>>[]{ 
    x => x.Prop1, 
    x => x.Prop2, 
    x => x.Prop3 
};
var notFields = new Expression<Func<MyEntity,string>>[]{ 
    x => x.Prop4, 
    x => x.Prop5 };

//----- 
var paramx = Expression.Parameter(query.ElementType);

//get fields to search for true
var whereColumnEqualsx = fields
    .Select(x => Expression.Invoke(x,paramx))
    .Select(x => Expression.Equal(x,Expression.Constant(search)))
    //you could change the above to use .Contains(...) || .StartsWith(...) etc.
    //you could also make it not case sensitive by 
    //wraping 'x' with a .ToLower() expression call, 
    //and setting the search constant to 'search.ToLower()'
    .Aggregate((x,y) => Expression.And(x,y));

//get fields to search for false
var whereColumnNotEqualsx = notFields
    .Select(x => Expression.Invoke(x,paramx))
    .Select(x => Expression.NotEqual(x, Expression.Constant(search)))
    //see above for the different ways to build your 'not' expression,
    //however if you use a .Contains() you need to wrap it in an Expression.Negate(...)
    .Aggregate((x,y) => Expression.Or(x,y));
    //you can change Aggregate to use Expression.And(...) 
    //if you want the query to exclude results only if the 
    //search string is in ALL of the negated fields.

var lambdax = Expression.Lambda(
    Expression.And(whereColumnEqualsx, whereColumnNotEqualsx), paramx);

var wherex = Expression.Call(typeof(Queryable)
    .GetMethods()
    .Where(x => x.Name == "Where")
    .First()
    .MakeGenericMethod(query.ElementType),
    query.Expression,lambdax);

//create query
var query2 = query.Provider.CreateQuery(wherex).OfType<MyEntity>();


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