2

I am trying to build an Entity Framework 4 Many to Many relationship filter, this is for a Dynamic Data project. I know that I need to build an Expression Tree at run time, and I am familiar with doing this for something like a simple where expression like this:

private MethodCallExpression BuiltMethodCall(IQueryable _query, Type _ObjType, string _ColumnToSearch, string _SearchValue)
{
ConstantExpression value = Expression.Constant(_SearchValue);
ParameterExpression _parameter = Expression.Parameter(_ObjType, "value");
MemberExpression _property = Expression.Property(_parameter, _ColumnToSearch);
BinaryExpression comparison = Expression.Equal(_property, value);
LambdaExpression lambda = Expression.Lambda(comparison, _parameter);

//Ex: Customers.Select(c => c).Where(value => (value.City == "Seattle"))
MethodCallExpression _where = Expression.Call(typeof(Queryable), "Where", new Type[] { _query.ElementType }, new Expression[] {
    _query.Expression,
    Expression.Quote(lambda)
});
return _where;

}

Just for simplicity, these examples use the Northwind database where there is a many to many join of (Customers <- CustomerCustomerDemo -> CustomerDemographics). What I am having trouble with is building the expression tree when there is a nested lambda like in the expression below, where I am retrieving all customers who have a specific customer demographic.

string custDemogID = "3";

//Get customers who have a particular demog
dynamic cust = context.Customers.Where(c => c.CustomerDemographics.Any(cd => cd.CustomerTypeID == custDemogID));

How do I build the Expression tree having a "where" call, that includes the nested lambda for the "Any"? Any help is appreciated, even if it is pseudo code on how to do it. There is not much out the on this, I am desperate, please help!

svick
  • 236,525
  • 50
  • 385
  • 514
Brian Tax
  • 81
  • 7
  • Have you tried creating the expression tree from code and inspecting how does it represent the nested lambda? – svick Oct 28 '12 at 09:03
  • I have looked at it in debug, and can see the nested lambda, but I am still trying to figure out how to create it dynamically. – Brian Tax Oct 29 '12 at 18:55

1 Answers1

6

Here is the solution that I have come up with, my biggest help came from Calling a Method from an Expression The biggest thing to over come was to create a generic "Any" method. The rest was just chaining the calls together.

private MethodCallExpression BuiltMethodCall(IQueryable _query, string CustTypeID, Type _ParentObjType, Type _ChildObjType, string strChildObj, string strChildCol)
{

//This function will build a dynamic linq expression tree representing the ling calls of:
//Customers.Where(c => c.CustomerDemographics.Any(cd => cd.CustomerTypeID = custTypeID))

ConstantExpression value = Expression.Constant(CustTypeID);

//Build the outer part of the Where clause
ParameterExpression parameterOuter = Expression.Parameter(_ParentObjType, "c");
MemberExpression propertyOuter = Expression.Property(parameterOuter, strChildObj);

//Build the comparison inside of the Any clause
ParameterExpression parameterInner = Expression.Parameter(_ChildObjType, "cd");
MemberExpression propertyInner = Expression.Property(parameterInner, strChildCol);

BinaryExpression comparison = Expression.Equal(propertyInner, value);
LambdaExpression lambdaInner = Expression.Lambda(comparison, parameterInner);

//Create Generic Any Method
Func<MethodInfo, bool> methodLambda = m => m.Name == "Any" && m.GetParameters().Length == 2;
MethodInfo method = typeof(Enumerable).GetMethods().Where(methodLambda).Single().MakeGenericMethod(_ChildObjType);

//Create the Any Expression Tree and convert it to a Lambda
MethodCallExpression callAny = Expression.Call(method, propertyOuter, lambdaInner);
LambdaExpression lambdaAny = Expression.Lambda(callAny, parameterOuter);

//Build the final Where Call
MethodCallExpression whereCall = Expression.Call(typeof(Queryable), "Where", new Type[] { _query.ElementType }, new Expression[] {
    _query.Expression,
    Expression.Quote(lambdaAny)
});

return whereCall;

}

Community
  • 1
  • 1
Brian Tax
  • 81
  • 7