I have the class:
public class Uid
{
public Guid Id { get; set; }
}
And i've an expression:
void TestMethod<T, TUid>(Expression<Func<T,IUid>> exp1) where TUid : Uid
{
....
}
I know that exp1.Body is a PropertyExpression, it's something like that: (s) => s.UidProperty where UidProperty is a property of Uid type. Having it I should create the following expression:
Expression<Func<T, Guid>> myExp = (s) => s.UidProperty.Id
How to do that?
We can use the following Compose method to take an expression that computes a value, and another expression that uses that output type as its input type, to create an expression that represents what would happen if the result of the first expression were passed to the second expression:
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);
}
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
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);
}
}
This allows us to write:
public Expression<Func<T, Guid>> TestMethod<T, TUid>(
Expression<Func<T,IUid>> expression)
where TUid : Uid
{
return expression.Compose(uid => uid.Id);
}
Related
I want a function Expression> AnyColumnContains(string[] value)
that iterates through all Columns of a table and checks an array of values against the columns and returns true only if every value is contained in any column.
i already have a function that matches every column against one value but i have problems extending it to check the columns against every value
This is what i've got:
Expression<Func<T, bool>> AnyColumnContains<T>(string value){
var p = Expression.Parameter(typeof(T), "entity");
var fieldAccessors = typeof(T)
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(f => f.PropertyType == typeof(string))
.Select(f => Expression.Property(p, f))
.ToArray();
var fieldArray = Expression.NewArrayInit(typeof(string), fieldAccessors);
var concatCall = Expression.Call(typeof(string).GetMethod(
"Concat", new[] { typeof(string[]) }), fieldArray);
var contains = Expression.Call(
concatCall,
typeof(string).GetMethod("Contains", new[] { typeof(string) }),
Expression.Constant(value));
return Expression.Lambda<Func<T, bool>>(contains, p);
}
I tried to use a own extension method and replaced Contains with it but the problem is that i use sqlite and the expression cannot be converted since the Provider doesn't know the methods
This is what i want:
Expression<Func<T, bool>> AnyColumnContains<T>(string[] values){
// ... //
var contains = // build Expression Tree that matches all values against concatCall and only returns true if all values are contained.
return Expression.Lambda<Func<T, bool>>(contains, p);
}
Rather than making an entirely new method from scratch, you can simply compose the method that you already have that's working.
We can use the following method to combine predicates together:
public static class PredicateBuilder
{
public static Expression<Func<T, bool>> True<T>() { return f => true; }
public static Expression<Func<T, bool>> False<T>() { return f => false; }
public static Expression<Func<T, bool>> Or<T>(
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var secondBody = expr2.Body.Replace(expr2.Parameters[0], expr1.Parameters[0]);
return Expression.Lambda<Func<T, bool>>
(Expression.OrElse(expr1.Body, secondBody), expr1.Parameters);
}
public static Expression<Func<T, bool>> And<T>(
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var secondBody = expr2.Body.Replace(expr2.Parameters[0], expr1.Parameters[0]);
return Expression.Lambda<Func<T, bool>>
(Expression.AndAlso(expr1.Body, secondBody), expr1.Parameters);
}
}
It relies on the following method to replace all instance of one expression with another:
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
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);
}
}
Now all we have to do is call the single value version of AnyColumnContains for each value and Or all of the results together:
public static Expression<Func<T, bool>> AnyColumnContains<T>(IEnumerable<string> values)
{
return values.Select(value => AnyColumnContains<T>(value))
.Aggregate((a, b) => a.Or(b));
}
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);
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);
}
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);
}
Using EF6, how would I bind a given Expression<Func<Row, string>> argument to an existing select expression, without having to rewrite every property binding using expression trees?
public IEnumerable<RowModel> GetRowModels(Expression<Func<Row, string>> textExpr)
{
return from row in MyDatabaseContext.MyTable
select new RowModel
{
RowID = row.ID,
CreatedDate = row.CreatedDate,
AnotherProperty = row.AnotherProperty,
Text = textExpr, // how do I bind this expression?
Value = row.OtherStuff.Where(os => os.ShouldUse).Select(os => os.Value).FirstOrDefault(),
AnotherValue = row.OtherStuff.Where(os => os.ShouldUseAgain).Select(os => os.Value).FirstOrDefault()
};
}
What you need here is a method to combine several expressions. Specifically, what we would like is a way to take an expression that maps a value and then also accept an expression that accepts the input of the first expression, and the output of the first expression, and computes a new value.
As an implementation of this method we can replace all instances of "the result of the first function" with the body of the first function; after that all that needs to be done is to ensure that both expressions are using the same Parameter instance.
public static Expression<Func<TFirstParam, TResult>>
Combine<TFirstParam, TIntermediate, TResult>(
this Expression<Func<TFirstParam, TIntermediate>> first,
Expression<Func<TFirstParam, 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], param)
.Replace(second.Parameters[1], newFirst);
return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}
The following code is used to replace all instances of an expression with another:
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
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);
}
}
As for using the function; it's simple enough. We call Combine on your textExpression, and then we can create a lambda accepting both the row and the text result of the first expression as parameters. This lets you write a lambda that's almost exactly like the one you already have, but where you can use the text parameter to assign the Text value:
public IEnumerable<RowModel> GetRowModels(
Expression<Func<Row, string>> textExpr)
{
return MyDatabaseContext.MyTable.Select(
textExpr.Combine((row, text) => new RowModel
{
RowID = row.ID,
CreatedDate = row.CreatedDate,
AnotherProperty = row.AnotherProperty,
Text = text, // how do I bind this expression?
Value = row.OtherStuff.Where(os => os.ShouldUse)
.Select(os => os.Value).FirstOrDefault(),
AnotherValue = row.OtherStuff.Where(os => os.ShouldUseAgain)
.Select(os => os.Value).FirstOrDefault()
}));
}
Forgive me, I'm not entirely sure my question is worded correctly.
I'm creating a search component where the user can search different fields with different operators... e.g. description.contains(keywords) and measurement.startsWith(yards).....
So here is what I have:
void SearchDescription(IQueryable<MyClass> results, string keywords)
{
switch(operator)
{
case "Contains":
results=results.Where(ele => ele.description.Contains(keywords));
break;
case "StartsWith":
results = results.Where(ele => ele.description.StartsWith(keywords));
break;
... and so on.....
}
}
Currently I have a method just as above for each field.... SearchDescription(), SearchID(), SearchMeasure(), etc. The only difference being the field/property name.
UPDATE
Upon further research possibly something like:
void Search(IQueryable<Entity> results, string keywords, Expression<Func<Entity>,object>> predicate)
{
results = results.Where(ele => predicate.Contains(keywords));
}
which could be called like:
Search(results, "my search terms", ele => ele.description);
This obviously doesn't work in it's current form, but maybe that is a clearer description of what I am after.
Thanks for all the responses so far.
This can be done by implementing a Compose method that will take two expressions and return an expression that acts as if it would invoke the first, then provide that as the parameter to the second:
void Search(IQueryable<Entity> results, string keywords,
Expression<Func<Entity, string>> selector)
{
results = results.Where(selector.Compose(obj => obj.Contains(keywords)));
}
To implement that we'll start off with a helper method that allows us to replace all instances of 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);
}
Using that tool it's as simple as a handful of replacements stuffed back together into a lambda:
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);
}
It also seems odd to filter the query to items where a single string with all of the words is contained in the given field. It seems more likely that you want to get items that contain any of a list of strings. That's different, and requires just a touch more work.
We can use a new class we'll call a PredicateBuilder to build up a filter that takes the logical OR of a bunch of other filters.
void Search(IQueryable<Entity> results, IEnumerable<string> keywords,
Expression<Func<Entity, string>> selector)
{
var finalFilter = keywords.Aggregate(
PredicateBuilder.False<Entity>(),
(filter, keyword) => filter.Or(
selector.Compose(obj => obj.Contains(keyword))));
results = results.Where(finalFilter);
}
We can implement this class using the Replace method defined earlier like so:
public static class PredicateBuilder
{
public static Expression<Func<T, bool>> True<T>() { return f => true; }
public static Expression<Func<T, bool>> False<T>() { return f => false; }
public static Expression<Func<T, bool>> Or<T>(
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var secondBody = expr2.Body.Replace(expr2.Parameters[0], expr1.Parameters[0]);
return Expression.Lambda<Func<T, bool>>
(Expression.OrElse(expr1.Body, secondBody), expr1.Parameters);
}
public static Expression<Func<T, bool>> And<T>(
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var secondBody = expr2.Body.Replace(expr2.Parameters[0], expr1.Parameters[0]);
return Expression.Lambda<Func<T, bool>>
(Expression.AndAlso(expr1.Body, secondBody), expr1.Parameters);
}
}
You can use System.Reflection to retrieve a PropertyInfo using the name of the wanted property. Note that Reflection is a bit slow and if used many times in a second, it can seriously slow your program down.
void Search(IQueryable<MyClass> results, string keywords, string propertyName)
{
PropertyInfo elePropInfo = ele.GetType().GetProperty(propertyName);
string elePropValue = (string)elePropInfo.GetValue(ele, null); // the second argument should be null for non-indexed properties
switch(operator)
{
case "Contains":
results = results.Where(ele => elePropValue.Contains(keywords));
break;
case "StartsWith":
results = results.Where(ele => elePropValue.StartsWith(keywords));
break;
// etc
}
}
More info on GetProperty() can be found here in MSDN: http://msdn.microsoft.com/en-us/library/kz0a8sxy(v=vs.110).aspx
If I have a specification defined as an Expression as below:
public Expression<Func<Foo, bool>> IsSuperhuman =
x => x.CanFly && x.HasXRayVision;
And I want to define another specification 'IsSuperheroine' with the logic 'is superhuman and is female', how can I reuse the existing specification within the new one?
Have you checked out predicate builder in LinqKit? It builds up expressions by letting you and and or expressions together.
Here's a way to do it :
Expression<Func<Foo, bool>> IsSuperhuman = x => x.CanFly && x.HasXRayVision;
Expression<Func<Foo, bool>> IsSuperheroine = AndAlso(IsSuperhuman, x => x.IsFemale);
...
public static Expression<Func<T, TResult>> AndAlso<T, TResult>(Expression<Func<T, TResult>> expr1, Expression<Func<T, TResult>> expr2)
{
var arg = Expression.Parameter(typeof(T), expr1.Parameters[0].Name);
var andExpr = Expression.AndAlso(
ReplaceParameter(expr1.Body, expr1.Parameters[0], arg),
ReplaceParameter(expr2.Body, expr2.Parameters[0], arg));
return Expression.Lambda<Func<T, TResult>>(andExpr, arg);
}
public static Expression ReplaceParameter(Expression expr, ParameterExpression oldParam, ParameterExpression newParam)
{
return new ReplaceParameterVisitor(oldParam, newParam).Visit(expr);
}
internal class ReplaceParameterVisitor : ExpressionVisitor
{
private ParameterExpression _oldParam;
private ParameterExpression _newParam;
public ReplaceParameterVisitor(ParameterExpression oldParam, ParameterExpression newParam)
{
_oldParam = oldParam;
_newParam = newParam;
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (node == _oldParam)
return _newParam;
return node;
}
}
It is probably not the simplest way to do it, but it works...