简体   繁体   中英

How can I compose an expression tree from the union of several expressions?

I'm trying to construct an IQueryable which will be evaluated by my entity model. I want to pass it two groups of lambdas and have it compose everything into a more complex expression tree, which is passed on to the database for execution.

Here's what I have so far:

public class FilterManager<T>
{
    public List<Expression<Func<T, bool>>> Inclusive { get; set; }
    public List<Expression<Func<T, bool>>> Exclusive { get; set; }

    public IQueryable<T> ApplyFilters(IQueryable<T> query)
    {
        var q = query;

        Exclusive.ForEach(exp => q = q.Where(exp)); //works fine
        Inclusive.ForEach(exp => /* ??? */); 

        return q;
    }

    //ctor, etc.
}

The idea here is that I add several Expressions to Inclusive , which "Ors" them together. For example, if T is an int , the code:

fm.Inclusive.Add(x => x > 1);
fm.Inclusive.Add(y => y < 5);

query = fm.ApplyFilters(query);

should have the same result set as:

query = query.Where(z => z > 1 || z < 5);

How can I get Inclusive to work without third-party tools such as PredicateBuilder? Third-party tools are usually fine, but I'd like to improve my understanding of how to compose expressions in .NET.

I also need to make sure that the tree won't be evaluated yet, so that I can do the filtering on the database. That means I'll need to produce something Entity Framework 4.0 can consume.

The closest match I can think of is this:

public IQueryable<T> ApplyFilters(IQueryable<T> query)
{
    IQueryable<T> q;

    if (!Inclusive.Any())
        q = query;
    else
    {
        q = Enumerable.Empty<T>();
        Inclusive.ForEach(exp => q = q.Union(query.Where(exp)));
    }

    Exclusive.ForEach(exp => q = q.Where(exp));

    return q;
}

But I'm almost sure that this will be very inefficient

Try something like this? I'm not sure I haven't tested it.

Inclusive.ForEach(exp => q = q.Union(q.Where(exp)));

Even though there's already an accepted answer, I would like to point out you can use predicate builder to combine the expressions with an Or . This will keep it as a simple query to the database.

http://www.albahari.com/nutshell/predicatebuilder.aspx

I haven't tested it on my entity model yet, so I don't know if it will be supported by EF, but the following works for L2O. It's just a slight change from Snowbear JIM-compiler 's code:

public IQueryable<T> ApplyFilters(IQueryable<T> query)
{
    Exclusive.ForEach(exp => query = query.Where(exp));

    if (Inclusive.Count == 0)
    {
        return query;
    }

    IQueryable<T> q = Enumerable.Empty<T>().AsQueryable<T>();
    Inclusive.ForEach(exp => q = q.Union(query.Where(exp)));

    return q;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM