At point (3) in my code I have defined a query called query1 in which I defined a .Where lambda expression. This query is in some way dynamic but still contains static elements, it always refers to the Type Employee and its (int) property ClientID.
Now I very much like to make the refering to the type and its property dynamic, based on the method parameters which by example are shown below point (1).
What I tried to so far is making the static part of the query defined under point (3) fully dynamic by replacing it with a more elaborate expression tree as written down in (4), (5) & (6). But when I try to add everything together it says I call .Where with wrong parameters. I don't know how to call .Where with the right parameters in order to create a fully dynamic select.
Does someone know to solve this problem? I have spent a day searching and haven't found a solution so far.
dsMain domainService = new dsMain();
//(1)i want to rewrite the following four variables to method-parameters
Type entityType = typeof(Employee);
String targetProperty = "ClientID";
Type entityProperty = typeof(Employee).GetProperty(targetProperty).PropertyType;
int idToDelete = 5;
//(2)create expression-function: idToDelete == entityType.targetProperty (in this case: Employee.ClientID)
ParameterExpression numParam = Expression.Parameter(entityProperty, targetProperty.Substring(0, 3));
ConstantExpression equalTarget = Expression.Constant(idToDelete, idToDelete.GetType());
BinaryExpression intEqualsID = Expression.Equal(numParam, equalTarget);
Expression<Func<int, bool>> lambda1 =
Expression.Lambda<Func<int, bool>>(
intEqualsID,
new ParameterExpression[] { numParam });
//(3)I want to create query1 fully dynamic, so defining Employee or an other type and its property at run time
WhereClause = lambda1.Compile();
IQueryable<Employee> employees = domainService.GetEmployees();
var query1 = employees.Where<Employee>(C => WhereClause.Invoke(C.ClientID)).Expression;
//(4)create the operand body {value(ASP.test_aspx).WhereClause.Invoke(E.ClientID)}
var operandbodyMethod = WhereClause.GetType().GetMethod("Invoke");
var operandbodyType = typeof(System.Boolean);
var operandbodyArgs1Expression = Expression.Parameter(entityType, entityType.Name.Substring(0, 1));
var operandbodyArgs1 = Expression.MakeMemberAccess(operandbodyArgs1Expression, entityType.GetMember(targetProperty)[0]);
var operandBodyObjectExp = Expression.Constant(this, this.GetType());
var operandbodyObject = Expression.MakeMemberAccess(operandBodyObjectExp, this.GetType().GetMember("WhereClause")[0]);
//(5)create the operand {E => value(ASP.test_aspx).WhereClause.Invoke(E.ClientID)}
var operandbody = Expression.Call(operandbodyObject, operandbodyMethod, operandbodyArgs1);
var operandParameter = Expression.Parameter(entityType, entityType.Name.Substring(0, 1));
var operandType = typeof(Func<,>).MakeGenericType(entityType, typeof(System.Boolean));
//(6)
var operand = Expression.Lambda(operandType, operandbody, new ParameterExpression[] { operandParameter });
var expressionType = typeof(Expression<>).MakeGenericType(operandType);
var completeWhereExpression = Expression.MakeUnary(ExpressionType.Quote, operand, expressionType);
//(7)the line below does not work
var query2 = employees.Where<Employee>(completeWhereExpression).Expression;
Thank you very much for reading my question!
If you have questions about my question, please ask them:)
This is quite hard to look at in isolation, but the first thing that occurs is that Compile looks out of place for IQueryable - that will rarely work (LINQ-to-Objects being the exception).
An equivalent to WhereClause.Invoke(C.ClientID) is to use Expression.Invoke to call a sub-expression, but even this is flakey: LINQ-to-SQL will support it, EF (in 3.5 at least) doesn't (maybe "didn't"; I haven't re-checked in 4.0). Ultimately, it would be more robust to create lambda1 as an Expression<Func<Employee,bool>> if possible:
ParameterExpression empParam = Expression.Parameter(typeof(Employee),"emp");
ConstantExpression equalTarget = Expression.Constant(idToDelete, idToDelete.GetType());
BinaryExpression intEqualsID = Expression.Equal(
Expression.PropertyOrField(empParam, targetProperty), equalTarget);
Expression<Func<Exmployee, bool>> lambda1 =
Expression.Lambda<Func<int, bool>>(
intEqualsID,
empParam);
Then pass this to Where:
var query1 = employees.Where(lambda1);
Related
I'm trying to write a generic wildcard Search for the ServiceStack.OrmLite.SqlExpressionVisitor that has the following signature:
public static SqlExpressionVisitor<T> WhereWildcardSearch<T> (this SqlExpressionVisitor<T> ev, Expression<Func<T,string>> field, string search)
where ev is the rest of the filter, field is the getter for the field to search by and search is the entered term.
Normally (non-generic) I would write the following:
if(search.StartsWith('*') && search.EndsWith('*'))
ev = ev.Where(x => x.foo.Contains(search.Trim('*')));
and of course also variants for x.foo.StartsWith or EndsWith.
Now I am searching for something like (pseudocode:)
ev = ev.Where(x => field(x).Contains(search.Trim('*')));
Of course I can't compile and call the expression directly, as this should be translated to Sql using Linq2Sql.
This is my code so far:
var getFieldExpression = Expression.Invoke (field, Expression.Parameter (typeof (T), "getFieldParam"));
var searchConstant = Expression.Constant (search.Trim('*'));
var inExp = Expression.Call (getFieldExpression, typeof(String).GetMethod("Contains"), searchConstant);
var param = Expression.Parameter (typeof (T), "object");
var exp = Expression.Lambda<Func<T, bool>> (inExp, param);
ev = ev.Where (exp);
Please don't tell me that I should directly write SQL with $"LIKE %search%" or something - I know that there are other ways, but solving this would help my understanding of Linq and Expressions in general and it bugs me when I can't solve it.
Here is how it can be done (I think it will be clear for you without much additional explanations what you did wrong, but if not - feel free to request a clarification):
// extract property name from passed expression
var propertyName = ((MemberExpression)field.Body).Member.Name;
var param = Expression.Parameter(typeof(T), "object");
var searchConstant = Expression.Constant(search.Trim('*'));
var contains = typeof(String).GetMethod("Contains");
// object.FieldName.Contains(searchConstant)
var inExp = Expression.Call(Expression.PropertyOrField(param, propertyName), contains, searchConstant);
// object => object.FieldName.Contains(searchConstant)
var exp = Expression.Lambda<Func<T, bool>>(inExp, param);
In response to comment. You have two expression trees: one is being passed to you and another one which you are building (exp). In this simple case they both use the same number of parameters and those parameters are of the same type (T). In this case you can reuse parameter from field expression tree, like this:
// use the same parameter
var param = field.Parameters[0];
var searchConstant = Expression.Constant(search.Trim('*'));
var contains = typeof(String).GetMethod("Contains");
// note field.Body here. Your `field` expression is "parameter => parameter.Something"
// but we need just "parameter.Something" expression here
var inExp = Expression.Call(field.Body, contains, searchConstant);
// pass the same parameter to new tree
var exp = Expression.Lambda<Func<T, bool>>(inExp, param);
In more complicated cases you might need to use ExpressionVisitor to replace parameters in one expression tree to reference to parameters from another (final) expression tree.
I have a DataClassesDataContext containing a group of tables, and I am trying to do lambda expression filtering dynamically using only the name of the tables and the names of the fields. Basically I want to find for each table if a row with a specific ID already exists.
If I knew the table ahead of time, I would use :
if (dataClassesDataContext.MYTABLEXs.SingleOrDefault(m => m.MYTABLEX_ID == MyId))
DoExists();
But as I am getting tables names MYTABLEX and MYTABLEY (and fields names MYTABLEX_ID and MYTABLEY_ID) as strings on the fly, I am trying to build the above filter at runtime.
I can access the table dynamically using :
Type tableType = Type.GetType(incommingtableName); // incommingtableName being looped over MYTABLEX, MYTABLEY , ...
var dbTable = dataClassesDataContext.GetTable(tableType);
But then I am stuck. How can I build a lambda expression that will behave something like :
if (dbTable.SingleOrDefault(m => m.incommingtableName_id == MyId))
DoExists();
Any idea ?
You can build an expression in runtime. And also you would need to have generic version of SingleOrDefault method. Here is example:
Type tableType = typeof (incommingtableName); // table type
string idPropertyName = "ID"; // id property name
int myId = 42; // value for searching
// here we are building lambda expression dynamically. It will be like m => m.ID = 42;
ParameterExpression param = Expression.Parameter(tableType, "m");
MemberExpression idProperty = Expression.PropertyOrField(param, idPropertyName);
ConstantExpression constValue = Expression.Constant(myId);
BinaryExpression body = Expression.Equal(idProperty, constValue);
var lambda = Expression.Lambda(body, param);
// then we would need to get generic method. As SingleOrDefault is generic method, we are searching for it,
// and then construct it based on tableType parameter
// in my example i've used CodeFirst context, but it shouldn't matter
SupplyDepot.DAL.SupplyDepotContext context = new SupplyDepotContext();
var dbTable = context.Set(tableType);
// here we are getting SingleOrDefault<T>(Expression) method and making it as SingleOrDefault<tableType>(Expression)
var genericSingleOrDefaultMethod =
typeof (Queryable).GetMethods().First(m => m.Name == "SingleOrDefault" && m.GetParameters().Length == 2);
var specificSingleOrDefault = genericSingleOrDefaultMethod.MakeGenericMethod(tableType);
// and finally we are exexuting it with constructed lambda
var result = specificSingleOrDefault.Invoke(null, new object[] { dbTable, lambda });
As possible optimization constructed lambda can be cached, so we wont need to build it each time, but it should work the same
I am trying to create a generic way of getting an EntityFramework object based on its own id without passing in a lambda expression as parameter to the method GetById(). For the code below the entity T is of type Message, is known to the class where GetById() is implemented and has a property MessageId along with several other properties. The MessageId name has been hard-coded in the example below as this is still experimental - extracting the id property name from T is quite easy to fix later.
I have been struggling to find a way to construct a simple LambdaExpression which has IQueryable<T> as parameter type and hope that someone would have a clue on how this could be done. The reason why I want IQueryable<T> is because my underlying channel factory provider requires this for more complex queries.
The line with var exp = Expression.Lambda<...> in the code below shows the expression function type definition which I want to end up with, but the line gives the exception:
Expression of type System.Boolean cannot be used for return type IQueryable
That's because the body has the Boolean type while my expression parameter queryParamtRet is of type IQueryable<Message>. Further, if I change the body type to be an IQueryable<Message>, I'm not able to find the property MessageId since the type is no longer type T as Message but type IQueryable<T>.
public T GetById(int id)
{
var queryParamLeft = Expression
.Parameter(typeof(System.Data.Entity.DbSet<T>), "o");
var queryParamRet = Expression
.Parameter(typeof(IQueryable<T>), "o");
var entityFrameworkType = Expression
.Parameter(typeof(T), "o");
var queryProperty = Expression
.PropertyOrField(entityFrameworkType, "MessageId");
var body = Expression
.Equal(queryProperty, Expression.Constant(id));
var exp = Expression
.Lambda<Func<System.Data.Entity.DbSet<T>, IQueryable<T>>>(
body,
queryParamRet);
var returnXml = DoWithChannel(channel
=> channel.Load(serializer.Serialize(exp)));
}
TLDR: Write out code that you want to create an expression for, then deliberately create the expression, starting with any inner expressions before combining them into the outer expression.
If you write your intended code as a function, it would look something like this
public static IQueryable<T> FilterADbSet(DbSet<T> dbSet)
{
return Queryable.Where<T>(dbSet, o => o.MessageId == 34);
}
It has one input parameter of type DbSet<T>, an output of type IQueryable<T> and it calls Queryable.Where<T> with parameters of the dbSet variable and an expression.
Working from the outside in, you first need to build the expression to pass to the where clause. You have already done that in your code.
Next you need to create a lambda expression for the where clause.
var whereClause = Expression.Equal(queryProperty, Expression.Constant(id));
var whereClauseLambda = Expression.Lambda<Func<T, bool>>(whereClause, entityFrameworkType);
Next, as the comments indicate, you need to use Expression.Call to create a body.
My end result with making your code work is below.
static Expression<Func<IQueryable<T>, IQueryable<T>>> WhereMethodExpression = v => v.Where(z => true);
static MethodInfo WhereMethod = ((MethodCallExpression)WhereMethodExpression.Body).Method;
public T GetById(int id)
{
var queryParamLeft = Expression
.Parameter(typeof(System.Data.Entity.DbSet<T>), "dbSet");
var entityFrameworkType = Expression
.Parameter(typeof(T), "entity");
var queryProperty = Expression
.PropertyOrField(entityFrameworkType, "MessageId");
var whereClause = Expression
.Equal(queryProperty, Expression.Constant(id));
var whereClauseLambda = Expression.Lambda<Func<T, bool>>(whereClause, entityFrameworkType);
var body = Expression.Call(
WhereMethod,
queryParamLeft,
whereClauseLambda
);
var exp = Expression
.Lambda<Func<System.Data.Entity.DbSet<T>, IQueryable<T>>>(
body,
queryParamLeft);
var returnXml = DoWithChannel(channel
=> channel.Load(serializer.Serialize(exp)));
}
I used an expression to fetch the MethodInfo object of Queryable.Where<T>
Your body expression needed queryParamLeft passed in. queryParamRet is not needed
I'm attempting to do a dynamic join in linq. Meaning that I only know at runtime what field the join will occur on.
I've done the following:
var itemParam = Expression.Parameter(typeof(E), "obj");
var entityAccess = Expression.MakeMemberAccess(Expression.Parameter(typeof(E), "obj"), typeof(E).GetMember(Field).First());
var lambda = Expression.Lambda(entityAccess, itemParam);
var q = dbSet.Join(context.Acl, lambda, acl => acl.ObjectID, (entity, acl) => new { Entity = entity, ACL = acl });
However this throws at compile time, even though lambda appears to be the right syntax telling me that it cannot convert from LambdaExpression to Expression<System.Func<E, int>>.
How do I get it to create the right expression dynamically that uses my field (i.e. property "Field" above in the typeof(E).GetMember(Field).First()) line?
Use Expression.Lambda<TDelegate>, so that you end up with the line
// obj => obj.Field
var lambda = Expression.Lambda<Func<E, int>>(entityAccess, itemParam);
Update
As per your comment, the reason the expression fails is because you are using two different parameters. You define itemParam, but then do not use it in Expression.MakeMemberAccess
Try the following instead:
// obj
var itemParam = Expression.Parameter(typeof(E), "obj");
// obj.Field
var entityAccess = Expression.MakeMemberAccess(itemParam, typeof(E).GetMember(Field).First());
I am learning expression trees in C#.
I am stuck now for a while:
string filterString = "ruby";
Expression<Func<string, bool>> expression = x => x == filterString;
How can I construct this expression by code? There is no sample how to capture a local variable. This one is easy:
Expression<Func<string, bool>> expression = x => x == "ruby";
This would be:
ParameterExpression stringParam = Expression.Parameter(typeof(string), "x");
Expression constant = Expression.Constant("ruby");
BinaryExpression equals = Expression.Equal(stringParam, constant);
Expression<Func<string, bool>> lambda1 =
Expression.Lambda<Func<string, bool>>(
equals,
new ParameterExpression[] { stringParam });
The debugger prints the following for (x => x == filterString) :
{x => (x ==
value(Predicate.Program+<>c__DisplayClass3).filterString)}
Thanks for shedding some light on this topic.
Capturing a local variable is actually performed by "hoisting" the local variable into an instance variable of a compiler-generated class. The C# compiler creates a new instance of the extra class at the appropriate time, and changes any access to the local variable into an access of the instance variable in the relevant instance.
So the expression tree then needs to be a field access within the instance - and the instance itself is provided via a ConstantExpression.
The simplest approach for working how to create expression trees is usually to create something similar in a lambda expression, then look at the generated code in Reflector, turning the optimization level down so that Reflector doesn't convert it back to lambda expressions.
This code wraps the expression in a closure Block that treats the local variable as a constant.
string filterString = "ruby";
var filterStringParam = Expression.Parameter(typeof(string), "filterString");
var stringParam = Expression.Parameter(typeof(string), "x");
var block = Expression.Block(
// Add a local variable.
new[] { filterStringParam },
// Assign a constant to the local variable: filterStringParam = filterString
Expression.Assign(filterStringParam, Expression.Constant(filterString, typeof(string))),
// Compare the parameter to the local variable
Expression.Equal(stringParam, filterStringParam));
var x = Expression.Lambda<Func<string, bool>>(block, stringParam).Compile();
An old question but I came to it when trying to do something similar building expressions for Linq-to-entities (L2E) In that case you cannot use Expression.Block as it cannot be parsed down to SQL.
Here is an explicit example following Jon's answer which would work with L2E. Create a helper class to contain the value of the filter:
class ExpressionScopedVariables
{
public String Value;
}
Build the tree thus:
var scope = new ExpressionScopedVariables { Value = filterString};
var filterStringExp = Expression.Constant(scope);
var getVariable = typeof(ExpressionScopedVariables).GetMember("Value")[0];
var access = Expression.MakeMemberAccess(filterStringExp, getVariable);
And then replace the constant in the original code with the member access expression:
BinaryExpression equals = Expression.Equal(stringParam, access);
Expression<Func<string, bool>> lambda1 =
Expression.Lambda<Func<string, bool>>(
equals,
new ParameterExpression[] { stringParam });