I am trying to build up an expression tree that I can feed into Linq2SQL so that it will generate a nice clean query. My purpose is to build a filter that takes an arbitrary
I like using linq to build epression trees, it makes me feel uber-powerfull, so I've added this, not as a complete answer to your question, but more a an elegant way to build up expression trees...
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>();
this example might help you. I guess the best is to build the expression without lambdas:
public class Entity
{
public Entity(string someField)
{
SomeField = someField;
}
public string SomeField { get; set; }
}
class Program
{
static void Main(string[] args)
{
var entities = new[] {new Entity("fooBar"), new Entity("barBaz"), new Entity("baz"), new Entity("foo")};
entities.Where(BuildExpression("ar","az").Compile())
.ToList()
.ForEach(e => Console.WriteLine(e.SomeField));
Console.ReadLine();
}
public static Expression<Func<Entity, bool>> BuildExpression(params string[] words)
{
var parameter = Expression.Parameter(typeof (Entity));
var matchs = words.Select(word =>
{
var property = Expression.Property(parameter, "SomeField");
var toLower = Expression.Call(property, "ToLower", new Type[] {});
var contains = Expression.Call(toLower, "Contains",
new Type[]{},
Expression.Constant(word));
return contains;
}).OfType<Expression>();
var body = matchs.Aggregate(Expression.Or);
return Expression.Lambda<Func<Entity, bool>>(body, new[] {parameter});
}
}
Please let me know if I should add more information to this answer.