How to pass a lambda expression to a method expecting lambda expression? - c#

I have an extension method to get a property name as string:
public static string GetPropertyName<T, TResult>(this T obj, Expression<Func<T, TResult>> propertyId)
{
return ((MemberExpression)propertyId.Body).Member.Name;
}
Now I have another method, expecting to pass in list (param) of this kind of property lamba expression.
I want this new method to reuse the 1st method, but can't figure out how to pass it over
public string Test<T>(params Expression<Func<T, object>>[] ps)
{
foreach (var p in ps)
{
var howToCodeThis = p.GetPropertyName(dummy => dummy);
expected usage:
var result = Test<Something>(request.Sorting
, x => x.prop1
, x => x.prop2
, x => x.prop3
);
Update:
Backs answer worked once I change my GetPropertyName to cater for UnaryExpression:
public static string GetPropertyName<T, TResult>(this T obj, Expression<Func<T, TResult>> propertyId)
{
if (propertyId.Body is MemberExpression)
return ((MemberExpression)propertyId.Body).Member.Name;
if (propertyId.Body is UnaryExpression)
{
var op = ((UnaryExpression)propertyId.Body).Operand;
return ((MemberExpression)op).Member.Name;
}
throw new NotImplementedException(string.Format("GetPropertyName - {0}", propertyId.Body.GetType().FullName));
}

var howToCodeThis = GetPropertyName(default(T), p);
OR
var howToCodeThis = default(T).GetPropertyName(p);
But I noteced, you don't use obj in GetPropertyName method.

Related

Trouble with building a C# EntityFramework IQueryable Expression

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);
}

Generic Linq to Entities filter method that accepts filter criteria and properties to be filtered

I've looked into many generic linq filtering questions and their answers here in SO but none of them satisfy my needs so I thought I should create a question.
I've created many of what I call "filter provider" classes, one for each entity class in my model, to provide a simplistic search for my application. I didn't want to go into more advanced solutions like Lucene.Net because a basic filtering with matching score would suffice.
Inside each one of these provider classes there are multiple methods that will receive the filtering terms and query specific properties, returning a score for each match based on the relevance of the property. Most methods will filter multiple properties at once, but not all.
Here are two of these methods:
private IQueryable<Retailer> MatchHighRelevanceFields(string searchTerm, IQueryable<Retailer> retailers)
{
var results = retailers.Where(r =>
(r.CompanyName != null && r.CompanyName.ToUpper().Contains(searchTerm))
|| (r.TradingName != null && r.TradingName.ToUpper().Contains(searchTerm))
);
return results;
}
private IQueryable<Retailer> MatchMediumRelevanceFields(string searchTerm, IQueryable<Retailer> retailers)
{
var results = retailers.Where(r =>
(r.Address.Street != null && r.Address.Street.ToUpper().Contains(searchTerm))
|| (r.Address.Complement != null && r.Address.Complement.ToUpper().Contains(searchTerm))
);
return results;
}
These methods are replicated ad nauseum throughout each provider class and I hope I could replace them for a single method that would receive the properties to be included in the query.
Something like:
public static IQueryable<T> Match<T>(string searchTerm, IQueryable<T> data, Expression<Func<T, string>> filterProperties)
{
var results = **build the query for each property in filterProperties**
return results;
}
But I really can't figure it out. I tried using reflection but it only worked with Linq to Objects and I need a solution for Linq to Entities.
So to solve this problem we need a few puzzle pieces first. The first puzzle piece is a method that can take an expression that computes a value, and then another expression that computes a new value taking the same type the first returns, and creates a new expression that represents the result of passing the result of the first function as the parameter to the second. This allows us to Compose expressions:
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);
}
This relies on the following tool to replace all instances 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);
}
}
We'll also need a tool to help us OR two predicate expressions 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);
}
}
Now that we have this we can use Compose on each property selector to map it from the property results to whether or not that property value is non-null and contains the search term. We can then OR all of those predicates together to get a filter for your query:
public static IQueryable<T> Match<T>(
IQueryable<T> data,
string searchTerm,
IEnumerable<Expression<Func<T, string>>> filterProperties)
{
var predicates = filterProperties.Select(selector =>
selector.Compose(value =>
value != null && value.Contains(searchTerm)));
var filter = predicates.Aggregate(
PredicateBuilder.False<T>(),
(aggregate, next) => aggregate.Or(next));
return data.Where(filter);
}
You can do it with expression trees but it's not as simple as you might think.
public static IQueryable<T> Match<T>(this IQueryable<T> data, string searchTerm,
params Expression<Func<T, string>>[] filterProperties)
{
var parameter = Expression.Parameter(typeof (T), "source");
Expression body = null;
foreach (var prop in filterProperties)
{
// need to replace all the expressions with the one parameter (gist taken from Colin Meek blog see link on top of class)
//prop.body should be the member expression
var propValue =
prop.Body.ReplaceParameters(new Dictionary<ParameterExpression, ParameterExpression>()
{
{prop.Parameters[0], parameter}
});
// is null check
var isNull = Expression.NotEqual(propValue, Expression.Constant(null, typeof(string)));
// create a tuple so EF will parameterize the sql call
var searchTuple = Tuple.Create(searchTerm);
var matchTerm = Expression.Property(Expression.Constant(searchTuple), "Item1");
// call ToUpper
var toUpper = Expression.Call(propValue, "ToUpper", null);
// Call contains on the ToUpper
var contains = Expression.Call(toUpper, "Contains", null, matchTerm);
// And not null and contains
var and = Expression.AndAlso(isNull, contains);
// or in any additional properties
body = body == null ? and : Expression.OrElse(body, and);
}
if (body != null)
{
var where = Expression.Call(typeof (Queryable), "Where", new[] {typeof (T)}, data.Expression,
Expression.Lambda<Func<T, bool>>(body, parameter));
return data.Provider.CreateQuery<T>(where);
}
return data;
}
public static Expression ReplaceParameters(this Expression exp, IDictionary<ParameterExpression, ParameterExpression> map)
{
return new ParameterRebinder(map).Visit(exp);
}
Now you need to have a expressionvisitor to make all the expressions use one parameter
//http://blogs.msdn.com/b/meek/archive/2008/05/02/linq-to-entities-combining-predicates.aspx
public class ParameterRebinder : ExpressionVisitor
{
private readonly IDictionary<ParameterExpression, ParameterExpression> _map;
public ParameterRebinder(IDictionary<ParameterExpression, ParameterExpression> map)
{
_map = map;
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (_map.ContainsKey(node))
{
return _map[node];
}
return base.VisitParameter(node);
}
}
Would use it like
var matches = retailers.Match("7", r => r.Address.Street, x => x.Address.Complement).ToList();
Warning - I checked this with linq to objects using the AsQueryable but didn't run it against EF.
You can use Linq.Dynamic to build the query.
public static IQueryable<T> Match<T>(
string searchTerm,
IQueryable<T> data,
params Expression<Func<T, string>>[] filterProperties) where T : class
{
var predicates = new List<string>();
foreach (var prop in filterProperties)
{
var lambda = prop.ToString();
var columnName = lambda.Substring(lambda.IndexOf('.') + 1);
var predicate = string.Format(
"({0} != null && {0}.ToUpper().Contains(#0))", columnName);
predicates.Add(predicate);
}
var filter = string.Join("||", predicates);
var results = data.Where(filter, searchTerm);
return results;
}
Usage.
var retailers = Match(
"asd", db.Retailers, r => r.CompanyName, r => r.TradingName);
var retailers = Match(
"asd", db.Retailers, r => r.Address.Street, r => r.Address.Complement);
Limitation.
The filter can only accept basic expression.
r => r.Name
r => r.PropA.Name
r => r.PropA.PropB.Name
Try to use Expressions like those all
http://www.codeproject.com/Articles/493917/Dynamic-Querying-with-LINQ-to-Entities-and-Express

How to implement method with expression parameter c#

I want to create a method like this:
var result = database.Search<EntityType>(x=>x.Name, "Entity Name field value");
result = database.Search<EntityType>(x=>x.Id, "Entity Id field value");
result = database.Search<EntityType2>(x=>x.Id, "Entity2 Id field value");
result = database.Search<EntityTypeAny>(x=>x.FieldAny, "EntityAny FieldAny value");
How can I implement this method?
You can turn a selector and value into a predicate using Expression.Equal:
static IQueryable<TSource> Search<TSource, TValue>(
this IQueryable<TSource> source,
Expression<Func<TSource,TValue>> selector,
TValue value)
{
var predicate = Expression.Lambda<Func<TSource,bool>>(
Expression.Equal(
selector.Body,
Expression.Constant(value, typeof(TValue))
), selector.Parameters);
return source.Where(predicate);
}
Then you just need to do something like:
var result = database.SomeEntities.Search(x => x.SomeProp, "value");
If you want to do it from the database, then that depends on what the database is; for example, with LINQ-to-SQL you could add an additional method:
static IQueryable<TSource> Search<TSource, TValue>(
this System.Data.Linq.DataContext database,
Expression<Func<TSource, TValue>> selector,
TValue value) where TSource : class
{
IQueryable<TSource> source = database.GetTable<TSource>();
return Search(source, selector, value);
}
and use:
var result = database.Search<SomeEntity, string>(x => x.SomeProp, "value");
frankly I think it is clearer to use the database.SomeEntities version, though.
I can only think of this (with 2 generic arguments)
public static IEnumerable<TModel> Search<TModel, TValue>(
Expression<Func<TModel, TValue>> expression,
TValue value
)
{
return new List<TModel>();
}
usage
var result = Search<EntityType, int>(x => x.Id, 1);
var result2 = Search<EntityType, string>(x => x.Name, "The name");
you can replace TValue with object to avoid the second generic argument, but I would stick with this.
Btw. this works great in conjunction with this little helper
public static class ExpressionHelpers
{
public static string MemberName<T, V>(this Expression<Func<T, V>> expression)
{
var memberExpression = expression.Body as MemberExpression;
if (memberExpression == null)
throw new InvalidOperationException("Expression must be a member expression");
return memberExpression.Member.Name;
}
}
Now you can get the Name of the Property (Id oder Name) in this example by calling
var name = expression.MemberName();
do you want types to dynamic
public ReturnType Read<ReturnType>(string FieldName, object dfVal)
{
if (Res.IsDBNull(Res.GetOrdinal(FieldName)))
return dfVal;
try {
return (ReturnType)Res.GetValue(Res.GetOrdinal(FieldName));
} catch (Exception ex) {
return dfVal;
}
}

C# Expression Tree Binding

So what I am trying to do is use expression trees to apply a predicate to each value in a collection (read map or list.All(predicate)). It appears that I am not getting the input parameter to the predicate bound to the value supplied by All, and I'm a little stuck. Here is the code (using linqpad) that I am working with::
public class SomeType
{
public IEnumerable<bool> Collection { get; set; }
}
void Main()
{
var list = new SomeType {
Collection = new List<bool> { true, true, true }
};
var functor = Compiler((SomeType t) => t.Collection, (bool x) => x);
functor(list).Dump();
}
MethodInfo FindMethod<TInput>(Type location, string name)
{
var handle = location
.GetMethods(BindingFlags.Static | BindingFlags.Public)
.Where(method => method.Name == name).First();
return handle.MakeGenericMethod(typeof(TInput));
}
Predicate<TObject> Compiler<TObject, TProperty>(
Expression<Func<TObject, IEnumerable<TProperty>>> selector,
Expression<Predicate<TProperty>> predicate)
{
var query = FindMethod<TProperty>(typeof(Enumerable), "All");
var expression = Expression.Call(query,
new Expression[] {
Expression.Invoke(selector, selector.Parameters),
Expression.Lambda<Func<TProperty, bool>>(predicate.Body,
Expression.Parameter(typeof(TProperty))),
});
return Expression.Lambda<Predicate<TObject>>(expression,
selector.Parameters).Compile();
}
Thanks and sorry if this was answered in another question (I looked for a while).
This does work, but I had to change the Predicate<TObject> to Func<TObject, bool>. If you want I can try to change it back.
static Predicate<TObject> Compiler<TObject, TProperty>(
Expression<Func<TObject, IEnumerable<TProperty>>> selector,
Expression<Func<TProperty, bool>> predicate)
{
var query = FindMethod<TProperty>(typeof(Enumerable), "All");
var expression = Expression.Call(
query,
Expression.Invoke(selector, selector.Parameters),
predicate);
return Expression
.Lambda<Predicate<TObject>>(expression, selector.Parameters)
.Compile();
}
5 minutes later... And if you really want to use Predicate<TObject>...
static Predicate<TObject> Compiler<TObject, TProperty>(
Expression<Func<TObject, IEnumerable<TProperty>>> selector,
Expression<Predicate<TProperty>> predicate)
{
var query = FindMethod<TProperty>(typeof(Enumerable), "All");
var predicateAsFunc = Expression.Lambda<Func<TProperty, bool>>(
predicate.Body,
predicate.Parameters);
var expression = Expression.Call(
query,
Expression.Invoke(selector, selector.Parameters),
predicateAsFunc);
return Expression
.Lambda<Predicate<TObject>>(expression, selector.Parameters)
.Compile();
}

Create an expression that will evaluate the result of a delegate and return the consequent or the alternate as the case may be

I want to retrieve the value of a property using a getter expression , but within that expression I want to evaluate a predicate and only return the value of the property if predicate evaluates to false, otherwise return a constant.
Something along the lines of (partially using code from here):
Expression<Func<U, bool>> exp = FuncToExpression(predicate);
var instance = Expression.Parameter(propertyInfo.DeclaringType, "instance");
var property = Expression.Property(instance, propertyInfo);
var convert = Expression.TypeAs(property, typeof(object));
var getLamba = Expression.Lambda(convert, instance);
var evaluate = Expression.Condition(exp, getLamba, Expression.Constant(alternate));
var lambda = Expression.Lambda(evaluate, instance);
return (Func<T, object>)lambda.Compile();
Any help here would be appreciated
Edit
More detail as per Jon's comment:
I am getting the following error on the evaluate variable :
{"Argument must be boolean"}
This is the FuncToExpression method :
private static Expression<Func<U, bool>> FuncToExpression<U>(Func<U, bool> predicate)
{
return argument => predicate(argument);
}
Edit 2
Complete Sample:
public class Test
{
public static void Main(string[] args)
{
TestPredicate test = new TestPredicate();
test.Number = 11;
Func<TestPredicate, object> callDelegate;
PropertyInfo info = typeof(TestPredicate).GetProperties().Where(a => a.Name == "Number").FirstOrDefault();
Func<int, bool> f = (x => x > 10 ? true : false);
if (info != null)
{
callDelegate = CreateValueGetDelegate<TestPredicate, int, int>(info, f, -1);
var item = (int) callDelegate(test);
Console.WriteLine(item); // expecting -1 here
}
Console.Read();
}
private static Func<T,object> CreateValueGetDelegate<T,U, S>(PropertyInfo propertyInfo, Func<U, bool> predicate, S alternate)
{
if (typeof(T) != propertyInfo.DeclaringType)
{
throw new ArgumentException();
}
Expression<Func<U, bool>> exp = FuncToExpression(predicate);
var instance = Expression.Parameter(propertyInfo.DeclaringType, "instance");
var property = Expression.Property(instance, propertyInfo);
var convert = Expression.TypeAs(property, typeof(object));
var getLamba = Expression.Lambda(convert, instance);
var evaluate = Expression.Condition(exp, getLamba, Expression.Constant(alternate));
var lambda = Expression.Lambda(evaluate, instance);
return (Func<T, object>)lambda.Compile();
}
private static Expression<Func<U, bool>> FuncToExpression<U>(Func<U, bool> predicate)
{
return argument => predicate(argument);
}
public class TestPredicate
{
public int Number { get; set; }
}
}
It would have helped if you'd said what currently going wrong, but I think you just need to get rid of the first Lambda call. I've made a few changes to the variable names too:
Expression<Func<U, bool>> test = FuncToExpression(predicate);
var parameter = Expression.Parameter(propertyInfo.DeclaringType, "instance");
var property = Expression.Property(parameter, propertyInfo);
var trueOption = Expression.TypeAs(property, typeof(object));
var falseOption = Expression.Constant(alternative);
var conditional = Expression.Condition(test, trueOption, falseOption);
var lambda = Expression.Lambda<Func<T, object>>(conditional, parameter);
return lambda.Compile();
If this doesn't work, please let us know in what way - ideally editing a short but complete sample program into your question.
This extension method will allow you to provide a selector (get the property), validator (validate the property) and a default value:
public static P GetValueOrDefault<T, P>(this T item, Func<T, P> selector, Func<P, bool> validator, P defaultValue)
{
if (item == null)
return defaultValue;
P value = selector(item);
if (validator == null || !validator(value))
return defaultValue;
return value;
}

Categories

Resources