pantonis
pantonis

Reputation: 6467

Null expression with dynamic LINQ

I have a method with the following signatures that return a C# expression

Expression<Func<T, bool>> GetExpression<T>(IList<Filter> filters)

Then the following code that uses Dynamic LINQ

using (TestContext tEntities = new TestContext())
{
    var filterExp = Exp.ExpressionBuilder.GetExpression<Client>(filters);
    var filteredCollection = tEntities.Client.Where(filterExp);

    IQueryable<Client> queryResult;
    if (filterExp == null)
        queryResult = tEntities.Client;
    else
        queryResult = tEntities.Client.Where(filterExp);
}

This is a simple scenario. I have queries that are 50 lines long sometimes more. I want to avoid having the same code twice with only difference using the where clause.

Does anyone knows if I achieve something different?

from product in context.Product.Where(deleg)
    .Include(x => x.Type)
    .Include(x => x.Category)
    .Include(x => x.WareHouse)
    .Include(x => x.Photos)
join f in context.Favorite on product.Id equals f.ProductFid into fg
from fgi in fg.Where(f => f.UserFid == userId).DefaultIfEmpty()
orderby product.Id descending
select new ProductngDto()
{
    ProductItem = product,
    FavoriteId = fgi != null ? fgi.Id : (long?)null
}).Skip(page * pageSize).Take(pageSize);

Thanks in advance

Upvotes: 2

Views: 679

Answers (1)

Christiaan Rakowski
Christiaan Rakowski

Reputation: 322

One of the nice things about IQueryable<T> and IEnumerable<T> is the fact that they are so abstract, you can easily chain them together. One solution could be to structure your code like this:

using (TestContext tEntities = new TestContext())
{
    var filterExp = Exp.ExpressionBuilder.GetExpression<Client>(filters);
    var filteredCollection = tEntities.Client.Where(filterExp);

    IQueryable<Client> queryResult = tEntities.Client;
    if (filterExp != null)
    {
        queryResult = queryResult.Where(filterExp);
    }
    //do something else with queryResult
}

This way you can continue using queryResult without having to know, or even care, about whether or not filterExp was applied.

For the second example, shuffling it around could look something like this:

var query = from p in context.Product
                      .Include(x => x.Type)
                      .Include(x => x.Category)
                      .Include(x => x.WareHouse)
                      .Include(x => x.Photos);

if (deleg != null)
{
    query = query.Where(deleg);
}

query = from product in query
        join f in context.Favorite on product.Id equals f.ProductFid into fg
        from fgi in fg.Where(f => f.UserFid == userId).DefaultIfEmpty();
        orderby product.Id descending
        select new ProductngDto()
        {
            ProductItem = product,
            FavoriteId = fgi != null ? fgi.Id : (long?)null
        }).Skip(page * pageSize).Take(pageSize);

Another option you have, is to check the filterExp for null and assign an "always true" lambda to it. If you control the GetExpression<T> method, you could add it in there as a last step. If you don't control it, you could do the null checking in the methods where you use it's result.

This will allow you to keep your code looking a bit cleaner, but the trade-off is a small performance hit for having to evaluate the lambda all the time.

Depending on how your expressions are typed, this might be as easy as:

if (filterExp == null)
{
    filterExp = (_) => true;
}

Upvotes: 2

Related Questions