Misha Zaslavsky
Misha Zaslavsky

Reputation: 9666

How to Combine Expressions

I am using EF and want to pass WCF Service a filter function. Everything is ok if I have one filter, but when I try to combine some expressions together I get an anoying error: The underlying connection was closed: A connection that was expected to be kept alive was closed by the server.

So here is my Filter object:

public class EventFilter
{
    public int CategoryId { get; set; }
    public int SubCategoryId { get; set; }
    public DateTime StartDate { get; set; }
    public int? Duration { get; set; }
    public string Address { get; set; }
}

And here is the filter function which works fine (one filter):

public IQueryable<Event> GetBySearch(EventFilter search)
{
    Expression<Func<Event, bool>> where = null;

    if (search.CategoryId != 0)
    {
        where = x => x.CategoryId == search.CategoryId;
    }

    return this.Context.Events.Where(where);
}

But if I want to expand it for two filters, it's not working and I can't understand how to fix it.

So, this is the combine function, which I believe is ok.

private static Expression<Func<TEntity, bool>> Combine<TEntity>(Expression<Func<TEntity, bool>> a, Expression<Func<TEntity, bool>> b)
{
    var and = Expression.AndAlso(a.Body, b.Body);
    var param = Expression.Parameter(typeof(TEntity));

    return Expression.Lambda<Func<TEntity, bool>>(and, param); ;
}

And here is the problem filter function (it's not working when the both filters happens:

public IQueryable<Event> GetBySearch(EventFilter search)
{
    Expression<Func<Event, bool>> where = null;

    if (search.CategoryId != 0)
    {
        where = x => x.CategoryId == search.CategoryId;
    }

    if (search.SubCategoryId != 0)
    {
        where = Combine<Event>(where, x => x.SubCategoryId == search.SubCategoryId);;
    }

    return this.Context.Events.Where(where);
}

I have tried many various of writing it, I have tried to enter a new object to the result, for example:

public IQueryable<Event> GetBySearch(EventFilter search)
{
    Expression<Func<Event, bool>> where = null;
    Expression<Func<Event, bool>> where2 = null;

    if (search.CategoryId != 0)
    {
        where = x => x.CategoryId == search.CategoryId;
    }

    if (search.SubCategoryId != 0)
    {
        where2 = x => x.SubCategoryId == search.SubCategoryId;
    }

    var result = Combine(where, where2);

    return this.Context.Events.Where(result);
}

After all I payed attention that this code works:

Expression<Func<Event, bool>> where3 = (x => x.CategoryId == search.CategoryId) && (x => x.SubCategoryId == search.SubCategoryId);

While this is not:

Expression<Func<Event, bool>> where3 = where && where2; //Compile time error.

Maybe here the problems starts, can someone help me? Thanks !!!

Upvotes: 1

Views: 104

Answers (1)

Scott Chamberlain
Scott Chamberlain

Reputation: 127563

Because multiple Where clauses are ANDed together and each clause returns a IQueryable<Event> you can just chain multiple clauses together like this.

public IQueryable<Event> GetBySearch(EventFilter search)
{
    IQueryable<Event> events = this.Context.Events; //(I assume Events is an IQueryable<Event>)

    if (search.CategoryId != 0)
    {
        events = events.Where(x => x.CategoryId == search.CategoryId);
    }

    if (search.SubCategoryId != 0)
    {
        events = events.Where(x => x.SubCategoryId == search.SubCategoryId);
    }

    return events;
}

This effectively does (assuming both id's where not 0)

this.Context.Events.Where(x => x.CategoryId == search.CategoryId).Where(x => x.SubCategoryId == search.SubCategoryId);

which is the same as

this.Context.Events.Where(x => x.CategoryId == search.CategoryId && x.SubCategoryId == search.SubCategoryId);

Upvotes: 2

Related Questions