Reputation: 11
I am trying to create a generic Exists() Command class that is able to construct a where clause for ANY combination of public properties (fields) of ANY LinqToSQL class (tables) in my datacontext (database).
If have written the following based on snippets from MSDN, and am finding that I cannot successfully run the code unless I explicitly declare the entity type (in this case, MyApp.Data.Organization) of the Expression.Lambda parameter of the Expression.Call statement.
In other words,
Expression.Lambda<Func<tblType,bool>>(predBody, new ParameterExpression[] { pe }));
does not work, but,
Expression.Lambda<Func<MyApp.Data.Organization,bool>>(predBody, new ParameterExpression[] { pe }));
does work. I want something like the former so the method remains generic to all LinqToSQL classes in my data context.
I have verified that all the code prior to the Expression.Call statement works fine and will generate a correct predicate. What do I need to change in order to keep the parameter Type generic so that the same code will work for any LinqToSQL class in the data context?
Revision for Clarity It seems my use of T as a variable was confusing things. Here is revised code:
OrganizationCriteria criteria = new OrganizationCriteria { OrganizationId = 2 };
using (var ctx = ContextManager<MyApp.Data.MyAppDataContext>.GetManager(myConnectionString, false)) {
IQueryable tbl = ctx.DataContext.GetTable(criteria.EntityType).AsQueryable();
Type tblType = tbl.ElementType;
ParameterExpression pe = Expression.Parameter( tblType, "Item" );
Expression left;
Expression right;
Expression prev = null;
Expression curr = null;
Expression predBody = null;
foreach ( KeyValuePair<string, object> kvp in criteria.StateBag ) {
prev = curr;
object val = kvp.Value;
if (val is System.String ) {
left = Expression.Call( pe, typeof( string ).GetMethod( "ToLower", System.Type.EmptyTypes ) );
right = Expression.Constant( kvp.Value.ToString() );
}
else {
left = Expression.Property( pe, tblType.GetProperty( kvp.Key ) );
right = Expression.Constant( kvp.Value, tblType.GetProperty( kvp.Key ).PropertyType );
}
curr = Expression.Equal( left, right );
if ( prev != null ) {
predBody = Expression.AndAlso( prev, curr );
}
else {
predBody = curr;
}
}
MethodCallExpression whereCall = Expression.Call(
typeof( Queryable ),
"Where",
new Type[] { tblType },
tbl.Expression,
Expression.Lambda<Func<tblType,bool>>(predBody, new ParameterExpression[] { pe }));
var results = tbl.Provider.CreateQuery( whereCall );
}
Upvotes: 0
Views: 2115
Reputation: 11
The answer is to use the non-generic overload of Expression.Lambda(). Thus, replacing:
Expression.Lambda<Func<tblType,bool>>(predBody, new ParameterExpression[] { pe })
with
Expression.Lambda(predBody, new ParameterExpression[] { pe })
did the trick, and I now have a totally generic method for querying any table on any fields.
Upvotes: 1
Reputation: 43056
Since tblType
is only known at runtime, you can only call Expression.Lambda<Func<T, bool>>()
by using reflection. Assuming that you know how to get the correct MethodInfo for the desired overload of the static Lambda
method:
var funcType = typeof(Func<,>).MakeGenericType(tblType, typeof(bool));
var genericMethodInfo = typeof(Expression).GetMethod("Lambda", ...
var methodInfo = genericMethodInfo.MakeGenericMethod(funcType);
var expression = (Expression)methodInfo.Invoke(...
but getting the right MethodInfo is far from trivial with all those overloads.
Upvotes: 0
Reputation: 10650
You'll have to pass the type parameter into the method containing your code (or as a type parameter for your class):
void SomeMethod<T>() {
...
Expression.Lambda<Func<T,bool>> ...
...
}
Upvotes: 0
Reputation: 9412
It looks like you're trying to access T
as a type - remember that it's actually a generic type parameter. If you want to use the actually type, use typeof(T)
. For example, this line:
ParameterExpression pe = Expression.Parameter(T, "Item");
should read as this:
ParameterExpression pe = Expression.Parameter(typeof(T), "Item");
Upvotes: 0
Reputation: 292615
In your code, T
is a variable, not a type; you can't use a variable as a generic type parameter (even if it's a variable of type Type
)
Upvotes: 1