Expression expression Tree splicing, Expression expression Expression
Recently, I encountered a problem in the project. The requirement is as follows:
To search for an existing user, you can retrieve it based on the user id or a part of the characters in the user name. In this case, there are three situations: id, with only one character in the user name, or all of them have.
We use the MVC + EF5.0 framework to construct a lambda expression as the query condition when performing queries at The BLL layer. But how can we build lambda to determine the query conditions? We know that a parameter like Express <Func <T, bool> can be a lambda expression, but the conditional concatenation method here cannot use the form of a delegate chain. of course, there is another solution. I write all the query conditions and then determine which one to use based on the passed ID or user name .. in this way, the judgment logic is chaotic and the code is lengthy. We want to find a method that dynamically concatenates query conditions.
That is, query conditions are dynamically spliced Based on the id or user name.
First, we need to know that the expression consists of two parts: Parameter and body. The first is the Parameter, the second is the expression body, and the expression body is a binary bitwise operation, that is, for example (left & right), and the value to be returned by left and right must be of the basic type, that is, the value that can be involved in bitwise operation. for example, in the lambda expression (a, B) => (), AB is a parameter, and the value returned by the expression body following the brackets can only be of the basic type. we need to construct an Expression Tree, mainly to construct this expression body. What type is this expression body? BinaryExpression type, we only need to construct this type, and then use Expression. and (left, right) or Expression. or () These two methods can be constructed. the two return values are the type objects of BinaryExpression. then we are using Expression. lambda <Func <T, bool> (BinaryExpression, Parameter) converts this expression tree to a lambda expression. this is the solution to this problem. Let's take a look at how we can implement it.
First, we define an expression variable.
Expression <Func <UserInfo, bool> where;
Then we start to construct labmda.
Next, let's construct parameters and necessary conditions, that is, c in c => () in lambda.
ParameterExpression param = Expression. Parameter (typeof (UserInfo), "c"); // c =>
// C => c. IsDelete = false: the condition is not deleted.
MemberExpression left1 = Expression. Property (param, typeof (UserInfo). GetProperty ("IsDelete"); construct c. IsDelete
ConstantExpression right1 = Expression. Constant (false); // construct a Constant false
BinaryExpression be = Expression. Equal (left1, right1); Build // c => c. IsDelete = false. This is now
Next we need to continue splicing this expression according to our condition, that is, the id and the user name string.
First, splice c. UserId = sid
If (! String. IsNullOrEmpty (Request ["sid"])
{
// C. UserId = sid
Int sid = int. Parse (Request ["sid"]);
// Construct the left expression c. UserId Based on the attribute of the Parameter
MemberExpression left2 = Expression. Property (param, typeof (UserInfo). GetProperty ("UserId "));
// Construct the right expression sid
ConstantExpression right2 = Expression. Constant (sid );
// Merge: cUserId = sid
BinaryExpression where2 = Expression. Equal (left2, right2 );
// Combine the condition with the previous condition: c. IsDelete = false & c. UserId = sid
Be = Expression. And (be, where2 );
}
Now let's splice the second condition.
As we have already said, the expression body must return a binary type, but this is a value-type string. What should I do?
According to the Expression method in MSDN, this method is found. Expression. Call ().
Then I read the example: |
|
What is it ?? |
We can use this call 'method to call a method of a type, and then generate a return value of the MethodCallExpression type. In this way, we will call the string. can the Contains method complete the expression we want?
See the following code.
// C. UserName. Contains (sname)
If (! String. IsNullOrEmpty (Request ["sname"])
{
String sname = Request ["sname"];
MemberExpression left3 = Expression. Property (param, typeof (UserInfo). GetProperty ("UserName"); // The attribute Expression c. UserName is constructed here.
ConstantExpression right3 = Expression. Constant (sname); // The Constant Expression sname is constructed here.
MethodCallExpression where3 = Expression. call (left3, typeof (string ). getMethod ("Contains"), right3); here we use the Call method to complete/c. userName. implementation of the lambda expression Contains (sname.
Be = Expression. And (be, where3); // concatenate the be Expression,
}
Where = Expression. Lambda <Func <UserInfo, bool> (be, param); // generate the final parameter Expression tree.
In this way, the expression tree is spliced.
As for the running result, we will not map it for everyone. We can run the same result as lambda. We can complete the query of two conditions.
The help class for this expression tree is encapsulated below. For details, refer.
Public class WhereHelper <T>
Where T: class
{
Private ParameterExpression param;
Private BinaryExpression filter;
Public WhereHelper ()
{
Param = Expression. Parameter (typeof (T), "c ");
// 1 = 1
Expression left = Expression. Constant (1 );
Filter = Expression. Equal (left, left );
}
Public Expression <Func <T, bool> GetExpression ()
{
Return Expression. Lambda <Func <T, bool> (filter, param );
}
Public void Equal (string propertyName, object value)
{
Expression left = Expression. Property (param, typeof (T). GetProperty (propertyName ));
Expression right = Expression. Constant (value, value. GetType ());
Expression result = Expression. Equal (left, right );
Filter = Expression. And (filter, result );
}
Public void Contains (string propertyName, string value)
{
Expression left = Expression. Property (param, typeof (T). GetProperty (propertyName ));
Expression right = Expression. Constant (value, value. GetType ());
Expression result = Expression. Call (left, typeof (string). GetMethod ("Contains"), right );
Filter = Expression. And (filter, result );
}
}
Of course, this help class has limited functions. If you have a helper, you can expand it on your own.
The technology mentioned in this article is all studied by our teacher, because he explained the principle and implementation to us after the study. I just sorted it out and gave it to everyone.