I'd like to write a statement like the following:
Expression<Func<AClass, bool>> filter = x => true;
Except instead of AClass
, I'd like to use a Type
variable determined at runtime. So, something conceptually like this:
Type aClassType = methodParameter.GetType();
Expression<Func<aClassType, bool>> filter = x => true;
Obviously the syntax will be quite a bit different. I'm assuming I'll need to use some sort of reflection or other fanciness.
End Goal
The end goal here is a bit complex, so I've simplified things immensely for the above example. The actual .Where
call that is going to use this delegate looks more like this:
var copy = iQ;
...
copy = copy.Where( p1 => iQ.Where( p2 => pgr2.Key == p1.Key && p2.DataField == column.DataField && p2.ColumnText.Contains( requestValue ) ).Any() );
All of the properties of p1 and p2 are properties of a parent class of the element type of the IQueryable
iQ. The Type variable I'd like to create will be the actual element type of iQ, i.e. the child class.
How do I do it?
Current Progress
Based on the answers below, I've written 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; }
}
}
That 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'd like to get this working before I try and replicate my complicated predicate.
I find the exception rather baffling, as the output for lambda.GetType()
matches the type in the exception almost exactly. The only difference is System.Boolean
versus bool
, but that shouldn't matter.