问题
So I'm attempting to build a semi complication Search expression, but I'm stuck trying to create a basic one. The expressions being used for getValueExpression look something like:
x => x.PropertyA != null ? x.PropertyA.ToShortDateString() : "" //nullable datetime
x => x.PropertyB //string property
x => x.PropertyC != null x.PropertyC.ToString() : "" //nullable int
Here is my function code, it currently errors when getValueExpression being of type Func that can't be compared to a string, which makes perfect sense and I understand why that is, but I'm having trouble figuring out how to make an expression that gets the value of getValueExpression to compare to the value being searched for. Any help or leads in the right direction would be greatly appreciated.
public static IQueryable<TSource> Search<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, string>> getValueExpression, string searchOption, string searchValue)
{
var searchValueExpression = Expression.Constant(searchValue);
var comparisonExpression = Expression.Equal(getValueExpression, searchValueExpression);
var lambdaExpression = Expression.Lambda<Func<TSource, bool>>(comparisonExpression);
return source.Where(lambdaExpression);
}
I've attempted similar things like this, but have met failure with incorrect arguments amount exception:
var getValueExpressionValue = Expression.Call(getValueExpression.Compile().Method, parameterValueExpression);
回答1:
Here is a method that will let you compose expressions; that is to say you can use the output of one expression as the input of another, creating a new expression taking the input that the first takes and the output that the second takes:
public static Expression<Func<TFirstParam, TResult>>
Compose<TFirstParam, TIntermediate, TResult>(
this Expression<Func<TFirstParam, TIntermediate>> first,
Expression<Func<TIntermediate, TResult>> second)
{
var param = Expression.Parameter(typeof(TFirstParam), "param");
var newFirst = first.Body.Replace(first.Parameters[0], param);
var newSecond = second.Body.Replace(second.Parameters[0], newFirst);
return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}
Which uses the following method to replace one expression with another:
internal class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
This lets you write:
public static IQueryable<TSource> Search<TSource>(this IQueryable<TSource> source,
Expression<Func<TSource, string>> getValueExpression,
string searchOption,
string searchValue)
{
var predicate = getValueExpression.Compose(value => value == searchValue);
return source.Where(predicate);
}
回答2:
Here is how you can do it :
public static IQueryable<TSource> Search<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, string>> getValueExpression, string searchOption, string searchValue)
{
// const searchValue
var searchValueExpression = Expression.Constant(searchValue);
// parameter x
var parameterExpression = Expression.Parameter(typeof(TSource));
// func(x)
var parameterGetValueExpression = Expression.Invoke(getValueExpression, parameterExpression);
// func(x) == searchValue
var comparisonExpression = Expression.Equal(parameterGetValueExpression, searchValueExpression);
// x => func(x) == searchValue
var lambdaExpression = Expression.Lambda<Func<TSource, bool>>(comparisonExpression, parameterExpression);
return source.Where(lambdaExpression);
}
来源:https://stackoverflow.com/questions/32121353/trouble-with-building-a-c-sharp-entityframework-iqueryable-expression