How to create an expression <Func <>> with type parameters from a type variable
I would like to write an expression like the following:
Expression<Func<AClass, bool>> filter = x => true; With the exception of AClass , I would like to use the Type variable defined at runtime. So something conceptual:
Type aClassType = methodParameter.GetType(); Expression<Func<aClassType, bool>> filter = x => true; Obviously, the syntax will be completely different. I guess I will need to use some kind of reflection or other quirkiness.
Final goal
The final goal here is a bit complicated, so I simplified the situation for the above example. The actual .Where call that this delegate will use is as follows:
var copy = iQ; ... copy = copy.Where( p1 => iQ.Where( p2 => pgr2.Key == p1.Key && p2.DataField == column.DataField && p2.ColumnText.Contains( requestValue ) ).Any() ); All properties p1 and p2 are properties of the parent class of the IQueryable iQ element type. The type variable that I would like to create will be the actual type of the iQ element, i.e. a child class.
How can I do it?
Current progress
Based on the answers below, I wrote this test code:
using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; namespace IQueryableWhereTypeChange { class Program { static void Main( string[] args ) { var ints = new List<ChildQueryElement>(); for( int i = 0; i < 10; i++ ) { ints.Add( new ChildQueryElement() { Num = i, Value = i.ToString() } ); } IQueryable<ChildQueryElement> theIQ = ints.AsQueryable(); Type type = typeof(ChildQueryElement); var body = Expression.Constant(true); var parameter = Expression.Parameter(type); var delegateType = typeof(Func<,>).MakeGenericType(type, typeof(Boolean)); var lambda = Expression.Lambda( delegateType, body, parameter ); Console.WriteLine( lambda.GetType() ); dynamic copy = theIQ; Type copyType1 = copy.GetType().GetGenericArguments()[ 0 ]; Type elementType1 = ((IQueryable)copy).ElementType; Console.WriteLine( "copyType1 : " + copyType1.ToString() ); Console.WriteLine( "elementType1 : " + elementType1.ToString() ); copy = Queryable.Where( copy, lambda ); Type copyType2 = copy.GetType().GetGenericArguments()[ 0 ]; Type elementType2 = ((IQueryable)copy).ElementType; Console.WriteLine( "copyType2 : " + copyType2.ToString() ); Console.WriteLine( "elementType2 : " + elementType2.ToString() ); } } public class ParentQueryElement { public int Num { get; set; } } public class ChildQueryElement : ParentQueryElement { public string Value { get; set; } } } This program has this output:
System.Linq.Expressions.Expression`1[System.Func`2[IQueryableWhereTypeChange.ChildQueryElement,System.Boolean]] copyType1 : IQueryableWhereTypeChange.ChildQueryElement elementType1 : IQueryableWhereTypeChange.ChildQueryElement Unhandled Exception: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: The best overloaded method match for 'System.Linq.Queryable.Where<IQueryableWhereTypeChange.ChildQueryElement>( System.Linq.IQueryable<IQueryableWhereTypeChange.ChildQueryElement>, System.Linq.Expressions.Expression<System.Func<IQueryableWhereTypeChange.ChildQueryElement,bool>> )' has some invalid arguments at CallSite.Target(Closure , CallSite , Type , Object , LambdaExpression ) at System.Dynamic.UpdateDelegates.UpdateAndExecute3[T0,T1,T2,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2) at IQueryableWhereTypeChange.Program.Main(String[] args) I would like to get this work before trying to reproduce a complex predicate.
I find the exception rather perplexing since the output for lambda.GetType() almost exactly the type of exception. The only difference is System.Boolean versus bool , but that doesn't matter.
You need to create the appropriate delegate type and then pass it to the Expression.Lambda method. For instance:
using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; class Test { static void Main() { var type = typeof(string); var body = Expression.Constant(true); var parameter = Expression.Parameter(type); var delegateType = typeof(Func<,>).MakeGenericType(type, typeof(bool)); dynamic lambda = Expression.Lambda(delegateType, body, parameter); Console.WriteLine(lambda.GetType()); // Expression<string, bool> } } Now, of course, your body will usually not just be a constant, but we donβt know what you need to do. From your edited question, it looks like you have some statically typed type knowledge, otherwise you will not be able to express this lambda expression. Thus, you need to create an expression tree manually to be equivalent to a lambda expression (using Expression.Property , etc.) or create one expression tree from what you know, and then use the expression tree visitor to adapt it to real type.
EDIT: Note that the lambda type must be dynamic in order for it to work as the second argument in Queryable.Where , otherwise the C # execution version will use the static variable type (which would just be LambdaExpression ) to execute at run time to restrict overload resolution. You want it to match the actual Expression<TDelegate> , which you cannot express at compile time, so you need to use dynamic .