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 );
}
The answer is to use the non-generic overload of Expression.Lambda(). Thus, replacing:
with
did the trick, and I now have a totally generic method for querying any table on any fields.