I have a Linq Expression, which may be altered depending on certain conditions. An example of what I would like to do (left blank the bit I am not sure about):
If I understand the question, then most likely here's the problem:
IEnumerable projects = unitOfWork.ProjectRepository.Get(filter);
Any work on projects
is going to be using Enumerable
, not Queryable
; it should probably be:
IQueryable projects = unitOfWork.ProjectRepository.Get(filter);
if(showArchived)
{
projects = projects.Where(p => p.Archived);
}
The latter is composable, and .Where
should work as you expect, building up a more restrictive query before sending it to the server.
Your other option is to rewrite the filter to combine before sending:
using System;
using System.Linq.Expressions;
static class Program
{
static void Main()
{
Expression> filter1 = x => x.A > 1;
Expression> filter2 = x => x.B > 2.5;
// combine two predicates:
// need to rewrite one of the lambdas, swapping in the parameter from the other
var rewrittenBody1 = new ReplaceVisitor(
filter1.Parameters[0], filter2.Parameters[0]).Visit(filter1.Body);
var newFilter = Expression.Lambda>(
Expression.AndAlso(rewrittenBody1, filter2.Body), filter2.Parameters);
// newFilter is equivalent to: x => x.A > 1 && x.B > 2.5
}
}
class Foo
{
public int A { get; set; }
public float B { get; set; }
}
class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
Or re-written in a way to allow convenient usage:
using System;
using System.Linq.Expressions;
static class Program
{
static void Main()
{
Expression> filter = x => x.A > 1;
bool applySecondFilter = true;
if(applySecondFilter)
{
filter = Combine(filter, x => x.B > 2.5);
}
var data = repo.Get(filter);
}
static Expression> Combine(Expression> filter1, Expression> filter2)
{
// combine two predicates:
// need to rewrite one of the lambdas, swapping in the parameter from the other
var rewrittenBody1 = new ReplaceVisitor(
filter1.Parameters[0], filter2.Parameters[0]).Visit(filter1.Body);
var newFilter = Expression.Lambda>(
Expression.AndAlso(rewrittenBody1, filter2.Body), filter2.Parameters);
return newFilter;
}
}
class Foo
{
public int A { get; set; }
public float B { get; set; }
}
class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}