I'm writing an Expression Parser to make my API more refactor friendly and less error prone. basicaly, I want the user to write code like that:
repository.Get(entity => entity.Id == 10);
instead of:
repository.Get<Entity>("Id", 10);
Extracting the member name from the left side of the binary expression was straight forward. The problems began when I tried to extract the value from the right side of the expression. The above snippet demonstrates the simplest possible case which involves a constant value but it can be much more complex involving closures and what not.
After playing with that for some time I gave up on trying to cover all the possible cases myself and decided to use the framework to do all the heavy lifting for me by compiling and executing the right side of the expression. the relevant part of the code looks like that:
public static KeyValuePair<string, object> Parse<T>(Expression<Func<T, bool>> expression)
{
var binaryExpression = (BinaryExpression)expression.Body;
string memberName = ParseMemberName(binaryExpression.Left);
obje开发者_StackOverflow社区ct value = ParseValue(binaryExpression.Right);
return new KeyValuePair<string, object>(memberName, value);
}
private static object ParseValue(Expression expression)
{
Expression conversionExpression = Expression.Convert(expression, typeof(object));
var lambdaExpression = Expression.Lambda<Func<object>>(conversionExpression);
Func<object> accessor = lambdaExpression.Compile();
return accessor();
}
Now, I get an InvalidOperationException (Lambda parameter not in scope) in the Compile line. when I googled for the solution I came up with similar questions that involved building an expression by hand and not supplying all the pieces, or trying to rely on parameters having the same name and not the same reference. I don't think that this is the case here because I'm reusing the given expression.
EDIT
This is one of the nonworking scenarios:
ExpressionParser.Parse(entity => entity.InternalClass.Id == entity.Id);
I would appreciate if someone will give me some pointers on this. Thank you.
The version with a constant works fine with the code as posted. Can you illustrate an example expression where it doesn't work?
When you see this, it means that your Right
is trying to use a parameter; there is only one parameter in a Expression<Func<T, bool>>
(the first one, of type T
). For example, I would expect the following to break (and it does):
// find people who are their own boss
var pair = Parse<Foo>(entity => entity.Id == entity.ManagerId);
Re the complexity; there are a lot of cases that can be parsed without compile; I use a "try it and fallback to Compile" strategy. See the Evaluate
and TryEvaluate
methods, here.
精彩评论