EF Core - Expression Tree Equivalent for IQueryable Search - c#

I have an initial workflow put together that allows me to perform inclusive searches for string properties of objects contained in an IQueryable:
public static IQueryable ApplySearch(this IQueryable queryable, string search)
{
// validation omitted for brevity
var expression = queryable
.Cast<object>()
.Where(item => item.SearchStringTree(search))
.Expression;
var result = queryable.Provider.CreateQuery(expression);
return result;
}
static bool SearchStringTree<T>(this T value, string search) =>
value.GetObjectStrings().Any(s => s.Contains(search.ToLower()));
static IEnumerable<string> GetObjectStrings<T>(this T value)
{
var strings = new List<string>();
var properties = value.GetType()
.GetProperties()
.Where(x => x.CanRead);
foreach (var prop in properties)
{
var t = prop.PropertyType.ToString().ToLower();
var root = t.Split('.')[0];
if (t == "system.string")
{
strings.Add(((string)prop.GetValue(value)).ToLower());
}
else if (!(root == "system"))
{
strings.AddRange(prop.GetValue(value).GetObjectStrings());
}
}
return strings;
}
Would it be possible to apply this concept in a way that Entity Framework can translate prior to DbContext execution?
I've been looking into potentially using Expression Trees to accomplish this.
Here's a working Repl.it showing the IQueryable implementation above.

You definitely need to build expression tree, basically multi or (C# ||) predicate expression for all (nested) string properties.
Something like this (expression version of your code):
public static class FilterExpression
{
public static IQueryable<T> ApplySearch<T>(this IQueryable<T> source, string search)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (string.IsNullOrWhiteSpace(search)) return source;
var parameter = Expression.Parameter(typeof(T), "e");
// The following simulates closure to let EF Core create parameter rather than constant value (in case you use `Expresssion.Constant(search)`)
var value = Expression.Property(Expression.Constant(new { search }), nameof(search));
var body = SearchStrings(parameter, value);
if (body == null) return source;
var predicate = Expression.Lambda<Func<T, bool>>(body, parameter);
return source.Where(predicate);
}
static Expression SearchStrings(Expression target, Expression search)
{
Expression result = null;
var properties = target.Type
.GetProperties()
.Where(x => x.CanRead);
foreach (var prop in properties)
{
Expression condition = null;
var propValue = Expression.MakeMemberAccess(target, prop);
if (prop.PropertyType == typeof(string))
{
var comparand = Expression.Call(propValue, nameof(string.ToLower), Type.EmptyTypes);
condition = Expression.Call(comparand, nameof(string.Contains), Type.EmptyTypes, search);
}
else if (!prop.PropertyType.Namespace.StartsWith("System."))
{
condition = SearchStrings(propValue, search);
}
if (condition != null)
result = result == null ? condition : Expression.OrElse(result, condition);
}
return result;
}
}
The non generic version is not much different - just instead of Where extension method you need to generate a "call" to it in the query expression tree:
public static IQueryable ApplySearch(this IQueryable source, string search)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (string.IsNullOrWhiteSpace(search)) return source;
var parameter = Expression.Parameter(source.ElementType, "e");
var value = Expression.Property(Expression.Constant(new { search }), nameof(search));
var body = SearchStrings(parameter, value);
if (body == null) return source;
var predicate = Expression.Lambda(body, parameter);
var filtered = Expression.Call(
typeof(Queryable), nameof(Queryable.Where), new[] { source.ElementType },
source.Expression, Expression.Quote(predicate));
return source.Provider.CreateQuery(filtered);
}
While this works, it's not much useful because all LINQ extensions methods (including AsEnumerable(),ToList()` etc.) work with generic interface.
Also in both cases, the type of the query element must be known in advance, e.g. T in the generic version, query.ElementType in the non generic version. This is because expression tree are processed in advance, when there are no "objects", hence it can't use item.GetType(). For the same reason, IQueryable translators like EF Core don't like Cast "calls" inside the query expression tree.

Related

LINQ method, which Dynamically ordering data by column name I send

What I want to do?
I want to create method, which Dynamically ordering data by column name I send.
First I created a project for testing. I see the project works super.
Test Project:
PropertyInfo pinfo = typeof(MockData).GetProperty(orderColumn);
switch (orderDirection)
{
case "asc":
mockDataList = q.OrderBy(o => pinfo.GetValue(o, null)).Skip(start).Take(length).ToList();
break;
case "desc":
mockDataList = q.OrderByDescending(o => pinfo.GetValue(o, null)).Skip(start).Take(length).ToList();
break;
}
After, I applied things, which I learned to my essential project that way:
Essential Project:
public IQueryable<T> RefactoringQuerybyPagination<T>(DataTablesRequestModel dataTablesRequestModel, IQueryable<T> query)
{
PropertyInfo pinfo = typeof(T).GetProperty(dataTablesRequestModel.OrderColumn);
switch (dataTablesRequestModel.OrderDirection)
{
case "asc":
query = query.OrderBy(o => pinfo.GetValue(o, null) != null).ThenBy(o => pinfo.GetValue(o, null));
break;
case "desc":
query = query.OrderByDescending(o => pinfo.GetValue(o, null) != null).ThenBy(o => pinfo.GetValue(o, null));
break;
}
query = query.Skip(dataTablesRequestModel.Start);
query = query.Take(dataTablesRequestModel.Length);
var test = query.ToList();
return query;
}
But it doesn't work and it gives me an error ("could not be translated...")
Solution I tried
public static IQueryable<T> OrderBy<T>(this IQueryable<T> items, string propertyName)
{
var typeOfT = typeof(T);
var parameter = Expression.Parameter(typeOfT, "parameter");
var propertyType = typeOfT.GetProperty(propertyName).PropertyType;
var propertyAccess = Expression.PropertyOrField(parameter, propertyName);
var orderExpression = Expression.Lambda(propertyAccess, parameter);
var expression = Expression.Call(typeof(Queryable), "OrderBy", new Type[] { typeOfT, propertyType }, items.Expression, Expression.Quote(orderExpression));
return items.Provider.CreateQuery<T>(expression);
}
It works but I can't manipulate expression. (like o = > o.Column.HasValue).
All day, I worked on this, I'm really tired. Can anyone help me?
Try to create a QueryableExtensions with the the following code:
//required using System.Linq;
//required using System.Linq.Expressions;
public static class QueryableExtensions
{
public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string columnName, bool isAscending = true)
{
if (String.IsNullOrEmpty(columnName))
{
return source;
}
ParameterExpression parameter = Expression.Parameter(source.ElementType, "");
MemberExpression property = Expression.Property(parameter, columnName);
LambdaExpression lambda = Expression.Lambda(property, parameter);
string methodName = isAscending ? "OrderBy" : "OrderByDescending";
Expression methodCallExpression = Expression.Call(typeof(Queryable), methodName,
new Type[] { source.ElementType, property.Type },
source.Expression, Expression.Quote(lambda));
return source.Provider.CreateQuery<T>(methodCallExpression);
}
Then, in the LINQ statement, we could use the above method to sort the records:
public IActionResult CategoryIndex()
{
//CategoryName Descending
var result1 = _context.Categories.OrderBy("CategoryName", false).Select(c=>c.CategoryName).ToArray();
//CategoryID Descending
var result2 = _context.Categories.OrderBy("CategoryID", false).Select(c => c.CategoryID).ToArray();
return View();
}
The screenshot as below:
You can use the Dynamic LINQ to achieve what you are trying to do in a lot easier and simplified way. You can find the NuGet package here - System.Linq.Dynamic.Core
From your code I'm assuming your scenario does not involve sorting/ordering on multiple columns. If so, you can create an extension method like below -
// you'll need to add this
// using System.Linq.Dynamic.Core;
public static class IQueryableExtension
{
public static IQueryable<T> ApplyOrder<T>(this IQueryable<T> source, string column, string direction)
{
if (string.IsNullOrWhiteSpace(column))
return source;
var pinfo = typeof(T).GetProperty(column);
if (pinfo == null)
return source;
var order = (direction == "desc") ? $"{column} desc" : column;
return source.OrderBy(order);
}
}
You can use it from your existing code like -
public IQueryable<T> RefactoringQuerybyPagination<T>(DataTablesRequestModel dataTablesRequestModel, IQueryable<T> query)
{
// here is the call
query = query.ApplyOrder(dataTablesRequestModel.OrderColumn, dataTablesRequestModel.OrderDirection);
query = query.Skip(dataTablesRequestModel.Start);
query = query.Take(dataTablesRequestModel.Length);
var test = query.ToList();
return query;
}

Better way to Sort a List by any property

My method receives all DataTables parameters to sort table by column clicked. I call this method from controller of each page list.
I'm looking for a better way to do this like a generic method for all types: string, int, decimal, double, bool (nullable or not). But I can't find it.
My current code:
public List<T> OrderingList<T>(List<T> list, DataTablesParam model)
{
var iColumn = model.Order.FirstOrDefault().Column;
var property = typeof(T).GetProperty(model.Columns.ToArray()[iColumn].Data);
var param = Expression.Parameter(typeof(T));
var final = Expression.Property(param, property);
var isDirAsc = model.Order.FirstOrDefault().Dir.Equals("asc");
if (property.PropertyType == typeof(string))
{
var lambda = Expression.Lambda<Func<T, string>>(final, param).Compile();
return isDirAsc ? list.OrderBy(lambda).ToList() : list.OrderByDescending(lambda).ToList();
}
else if (property.PropertyType == typeof(int))
{
var lambda = Expression.Lambda<Func<T, int>>(final, param).Compile();
return isDirAsc ? list.OrderBy(lambda).ToList() : list.OrderByDescending(lambda).ToList();
}
else if (property.PropertyType == typeof(bool))
{
var lambda = Expression.Lambda<Func<T, bool>>(final, param).Compile();
return isDirAsc ? list.OrderBy(lambda).ToList() : list.OrderByDescending(lambda).ToList();
}
else if (property.PropertyType == typeof(decimal))
{
var lambda = Expression.Lambda<Func<T, decimal>>(final, param).Compile();
return isDirAsc ? list.OrderBy(lambda).ToList() : list.OrderByDescending(lambda).ToList();
}
else if (property.PropertyType == typeof(double))
{
var lambda = Expression.Lambda<Func<T, double>>(final, param).Compile();
return isDirAsc ? list.OrderBy(lambda).ToList() : list.OrderByDescending(lambda).ToList();
}
return list;
}
I want to do something like this: (But this code doesn't work)
public List<T> OrderingList<T>(List<T> list, DataTablesParam model)
{
var iColumn = model.Order.FirstOrDefault().Column;
var property = typeof(T).GetProperty(model.Columns.ToArray()[iColumn].Data);
var param = Expression.Parameter(typeof(T));
var final = Expression.Property(param, property);
var isDirAsc = model.Order.FirstOrDefault().Dir.Equals("asc");
var lambda = Expression.Lambda<Func<T, dynamic>>(final, param).Compile();
return isDirAsc ? list.OrderBy(lambda).ToList() : list.OrderByDescending(lambda).ToList();
}
You can just call the Enumerable.OrderBy method using reflection. That way, you don’t have to know the type at compile-time. To do that, you just need to get the method, and create a generic method using the property’s type:
private IEnumerable<T> Sort<T> (List<T> list, string propertyName)
{
MethodInfo orderByMethod = typeof(Enumerable).GetMethods().First(mi => mi.Name == "OrderBy" && mi.GetParameters().Length == 2);
PropertyInfo pi = typeof(T).GetProperty(propertyName);
MethodInfo orderBy = orderByMethod.MakeGenericMethod(typeof(T), pi.PropertyType);
ParameterExpression param = Expression.Parameter(typeof(T));
Delegate accessor = Expression.Lambda(Expression.Property(param, pi), param).Compile();
return (IEnumerable<T>)orderBy.Invoke(null, new object[] { lst, accessor });
}
Note that I abstracted out the stuff about your model to keep this method generic enough. It can basically sort by any property on a list by just specifying the property name. Your original method would then look like this:
public List<T> OrderingList<T>(List<T> list, DataTablesParam model)
{
var iColumn = model.Order.FirstOrDefault().Column;
string propertyName = model.Columns.ToArray()[iColumn].Data;
return Sort(list, propertyName).ToList();
}
It's works fine for me: (Thanks #Poke)
https://stackoverflow.com/a/31393168/5112444
My final method:
private IEnumerable<T> Sort<T>(IEnumerable<T> list, string propertyName, bool isAsc)
{
MethodInfo orderByMethod = typeof(Enumerable).GetMethods().First(mi => mi.Name == (isAsc ? "OrderBy" : "OrderByDescending") && mi.GetParameters().Length == 2);
PropertyInfo pi = typeof(T).GetProperty(propertyName);
MethodInfo orderBy = orderByMethod.MakeGenericMethod(typeof(T), pi.PropertyType);
ParameterExpression param = Expression.Parameter(typeof(T));
Delegate accessor = Expression.Lambda(Expression.Call(param, pi.GetGetMethod()), param).Compile();
return (IEnumerable<T>)orderBy.Invoke(null, new object[] { list, accessor });
}
Your suggested method almost works. You need to change two things in order to make it work:
public List<T> OrderingList<T>(List<T> list, DataTablesParam model)
{
var iColumn = model.Order.FirstOrDefault().Column;
var property = typeof(T).GetProperty(model.Columns.ToArray()[iColumn].Data);
var param = Expression.Parameter(typeof(T), "p");
Expression final = Expression.Property(param, property);
// Boxing of value types
if (property.PropertyType.IsValueType) {
final = Expression.MakeUnary(ExpressionType.Convert, final, typeof(object));
}
var isDirAsc = model.Order.FirstOrDefault().Dir.Equals("asc");
// VVVVVV
var lambda = Expression.Lambda<Func<T, object>>(final, param).Compile();
return isDirAsc
? list.OrderBy(lambda).ToList()
: list.OrderByDescending(lambda).ToList();
}
Instead of using dynamic use object, since every type is an object.
If you have a value type, you need a boxing operation, i.e. you must cast the value to object (object)i. This is done with a unary convert operation:
Expression final = Expression.Property(param, property);
if (property.PropertyType.IsValueType) {
final = Expression.MakeUnary(ExpressionType.Convert, final, typeof(object));
}
Note also that final is declared explicitly as Expression, since the expression type might change from property to unary expression.
Beyond just not being very generic, your solution also requires a lot of extra memory because you're copying the list with LINQ. You can avoid this using List.Sort.
I would do:
static void SortBy<T>(List<T> list, MemberInfo member, bool desc)
{
Comparison<T> cmp = BuildComparer<T>(member, desc);
list.Sort(cmp);
}
static Comparison<T> BuildComparer<T>(MemberInfo member, bool desc)
{
var left = Expression.Parameter(typeof(T));
var right = Expression.Parameter(typeof(T));
Expression cmp = Expression.Call(
Expression.MakeMemberAccess(desc ? right : left, member),
"CompareTo",
Type.EmptyTypes,
Expression.MakeMemberAccess(desc ? left : right, member));
return Expression.Lambda<Comparison<T>>(cmp, left, right).Compile();
}
I found a better way to do this. I had to do 3 steps:
1 - Add the package "Linq Dynamic" in project:
Install-Package System.Linq.Dynamic.Library
2 - Import the package in Class:
using System.Linq.Dynamic;
3 - Order list by the string name of property:
list.OrderBy(stringPropertyName); //asc
list.OrderBy(stringPropertyName + " descending"); //des
It work perfectly for me.

Subquery with "ANY" and local array generate nested too deep SQL Statement

public IEnumerable<Table1> GetMatchingTable1(string param, double[] Thicknesses)
{
return DBContext.Table1.Where(c => c.Field1 == param
&& Thicknesses.Any(Thickness => Thickness >= c.MinThickness && Thickness <= c.MaxThickness))
.ToList();
}
Above query return the following exception. "Some part of your SQL statement is nested too deeply. Rewrite the query or break it up into smaller queries."
So far, all my research on the web for this error pointed toward replacing "ANY" with "CONTAINS". Here is one site where they fix the problem using this solution : http://blog.hompus.nl/2010/08/26/joining-an-iqueryable-with-an-ienumerable/
But in my case, "CONTAINS" doesn't seem usable since I check a RANGE with Min and Max.
How should this query be written to have a proper SQL Statement generated by LinqToEntity?
Thanks
You could try to build the query dynamically:
public IEnumerable<Table1> GetAllCoilLengthSettingsWithChilds(string param, double[] Thicknesses)
{
// Base query
var query = LinqKit.Extensions.AsExpandable(DBContext.Table1.Where(c => c.Field1 == param));
// All the various || between the Thickness ranges
var predicate = LinqKit.PredicateBuilder.False<Table1>();
foreach (double th in Thicknesses)
{
// Don't want a closure around th
double th2 = th;
predicate = predicate.Or(c => th2 >= c.MinThickness && th2 <= c.MaxThickness);
}
// This is implicitly in && with the other Where
query = query.Where(predicate);
return query.ToList();
}
The PredicateBuilder helps you build an || query. Take it from the LinqKit (source available)
I've tested it with 1000 parameters (but they where DateTime, and I didn't have other query pieces), and it seems to work. Note that the program uses another extension of LinqPad, AsExpandable, used to make the PredicateBuilder "trick" work. Note that I'm using EF 6.1.3, so your mileage may vary.
If you don't want to use LinqKit, I'm appending my version of PredicateBuilder. It doesn't require the use of AsExpandable(), but its syntax is slightly different:
public class PredicateBuilder<T>
{
// We share a single parameter for all the PredicatBuilder<T>
// istances. This isn't a proble, because Expressions are immutable
protected static readonly ParameterExpression Parameter = Expression.Parameter(typeof(T), "x");
protected Expression Current { get; set; }
// Returns an empty PredicateBuilder that, if used, is true
public PredicateBuilder()
{
}
// Use it like this: .Where(predicate) or .Any(predicate) or
// .First(predicate) or...
public static implicit operator Expression<Func<T, bool>>(PredicateBuilder<T> predicate)
{
if (object.ReferenceEquals(predicate, null))
{
return null;
}
// Handling of empty PredicateBuilder
Expression current = predicate.Current ?? Expression.Constant(true);
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(current, Parameter);
return lambda;
}
public static implicit operator PredicateBuilder<T>(Expression<Func<T, bool>> expression)
{
var predicate = new PredicateBuilder<T>();
if (expression != null)
{
// Equivalent to predicate.Or(expression)
predicate.And(expression);
}
return predicate;
}
public void And(Expression<Func<T, bool>> expression)
{
if (expression == null)
{
throw new ArgumentNullException("expression");
}
var expression2 = new ParameterConverter(expression.Parameters[0], Parameter).Visit(expression.Body);
this.Current = this.Current != null ? Expression.AndAlso(this.Current, expression2) : expression2;
}
public void Or(Expression<Func<T, bool>> expression)
{
if (expression == null)
{
throw new ArgumentNullException("expression");
}
var expression2 = new ParameterConverter(expression.Parameters[0], Parameter).Visit(expression.Body);
this.Current = this.Current != null ? Expression.OrElse(this.Current, expression2) : expression2;
}
public override string ToString()
{
// We reuse the .ToString() of Expression<Func<T, bool>>
// Implicit cast here :-)
Expression<Func<T, bool>> expression = this;
return expression.ToString();
}
// Small ExpressionVisitor that replaces the ParameterExpression of
// an Expression with another ParameterExpression (to make two
// Expressions "compatible")
protected class ParameterConverter : ExpressionVisitor
{
public readonly ParameterExpression From;
public readonly ParameterExpression To;
public ParameterConverter(ParameterExpression from, ParameterExpression to)
{
this.From = from;
this.To = to;
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (node == this.From)
{
node = this.To;
}
return base.VisitParameter(node);
}
}
}
public static class PredicateBuilder
{
// The value of source isn't really necessary/interesting. Its type
// is :-) By passing a query you are building to Create, the compiler
// will give to Create the the of the object returned from the query
// Use it like:
// var predicate = PredicateBuilder.Create<MyType>();
// or
// var predicate = PredicateBuilder.Create(query);
public static PredicateBuilder<T> Create<T>(IEnumerable<T> source = null)
{
return new PredicateBuilder<T>();
}
// Useful if you want to start with a query:
// var predicate = PredicateBuilder.Create<MyType>(x => x.ID != 0);
// Note that if expression == null, then a new PredicateBuilder<T>()
// will be returned (that by default is "true")
public static PredicateBuilder<T> Create<T>(Expression<Func<T, bool>> expression)
{
// Implicit cast to PredicateBuilder<T>
return expression;
}
}
Use it like:
var predicate = PredicateBuilder.Create(query);
and then everything is the same (but remove the LinqKit.Extensions.AsExpandable part)

Creating Sorting Func<IQueryable<T>, IOrderedQueryable<T>>?

I wanna create a sorting generic Func Creating Func<IQueryable<T>, IOrderedQueryable<T>> in an extension method :
public static Func<IQueryable<T>, IOrderedQueryable<T>> GetOrderByFunc<T>(this KeyValuePair<string, SortingType> keyValuePair)
{
//I expect the following result
//Func<IQueryable<T>, IOrderedQueryable<T>> orderby = q => q.OrderByDescending(c => c.Name);
//keyValuePair.Key is name of property in type of T that I should sort T by it
switch (keyValuePair.Value)
{
case SortingType.Ascending:
// Creating Ascending Sorting Func
break;
case SortingType.Descending:
// Creating Descending Sorting Func
break;
default :
break;
}
}
Could you guide me, how I can do it?
Edit:
this sorting also, contains of count of a T's navigation property.
e.g:
// keyValuePair.Key equals "User.Count"
// User is a navigation property of T
Func<IQueryable<T>, IOrderedQueryable<T>> orderby = q => q.OrderByDescending(c => c.User.Count);
Edit:
I changed GetSelector as the following, but an exception has occurred in bodyExpression.
public static Expression GetSelector<T>(string propertyName)
{
ParameterExpression parameter = Expression.Parameter(typeof(T));
if (propertyName.Contains("."))
{
propertyName = propertyName.Substring(0, propertyName.IndexOf("."));
Type navigationPropertyCollectionType = typeof(T).GetProperty(propertyName).PropertyType;
if (navigationPropertyCollectionType.GetGenericTypeDefinition() == typeof(ICollection<>))
{
Expression countParameter = Expression.Parameter(navigationPropertyCollectionType, "c");
MemberExpression countExpression = Expression.Property(countParameter, "Count");
//Exception: Instance property 'Users(ICollection`1)' is not defined for type 'System.Int32'
var bodyExpression = Expression.Property(countExpression, propertyName, countParameter);
return Expression.Lambda(bodyExpression, parameter);
}
}
MemberExpression bodyMemberExpression = Expression.Property(parameter, typeof(T).GetProperty(propertyName));
return Expression.Lambda(bodyMemberExpression, parameter);
}
So the first thing that we'll need is a method that can get the selector expression that selects out that property value when given the property name. It will need to build the expression from scratch:
public static Tuple<Expression, Type> GetSelector<T>(IEnumerable<string> propertyNames)
{
var parameter = Expression.Parameter(typeof(T));
Expression body = parameter;
foreach (var property in propertyNames)
{
body = Expression.Property(body,
body.Type.GetProperty(property));
}
return Tuple.Create(Expression.Lambda(body, parameter) as Expression
, body.Type);
}
Note that since this results in a chain of properties the method also returns the type of the final property, as that wouldn't be a particularly easy bit of information to access from the caller's perspective.
Because we don't know the return type of the property when calling Selector so we have no choice but to leave the return type of this method typed to Expression. We can't cast it to an Expression<Func<T, Something>>. We could have it return a Expression<Func<T, object>>, and that would work for all properties that select out a reference type, but this wouldn't be able to box value types, so it would throw a runtime exception in those cases.
Now, because we don't know the exact type of that expression we can't call OrderBy or OrderByDescending directly. We need to grab those methods through reflection and use MakeGenericMethod so that they can be created using the proper type based on inspection of that property using reflection.
public static Func<IQueryable<T>, IOrderedQueryable<T>> GetOrderByFunc<T>(
this Tuple<IEnumerable<string>, SortingType> sortCriteria)
{
var selector = GetSelector<T>(sortCriteria.Item1);
Type[] argumentTypes = new[] { typeof(T), selector.Item2 };
var orderByMethod = typeof(Queryable).GetMethods()
.First(method => method.Name == "OrderBy"
&& method.GetParameters().Count() == 2)
.MakeGenericMethod(argumentTypes);
var orderByDescMethod = typeof(Queryable).GetMethods()
.First(method => method.Name == "OrderByDescending"
&& method.GetParameters().Count() == 2)
.MakeGenericMethod(argumentTypes);
if (sortCriteria.Item2 == SortingType.Descending)
return query => (IOrderedQueryable<T>)
orderByDescMethod.Invoke(null, new object[] { query, selector.Item1 });
else
return query => (IOrderedQueryable<T>)
orderByMethod.Invoke(null, new object[] { query, selector.Item1 });
}
You would need to use Expressions
public static Func<IQueryable<T>, IOrderedQueryable<T>> GetOrderByFunc<T>(this KeyValuePair<string, SortingType> keyValuePair)
{
Func<IQueryable<T>, IOrderedQueryable<T>> result;
var p1 = Expression.Parameter(typeof (T), "p1");
var prop = Expression.PropertyOrField(p1, keyValuePair.Key);
var lambada = Expression.Lambda<Func<T, object>>(prop, new ParameterExpression[] {p1});
//keyValuePair.Key is name of property in type of T that I should sort T by it
switch (keyValuePair.Value)
{
case SortingType.Ascending:
result = source => source.OrderBy(lambada);
break;
case SortingType.Descending:
result = source => source.OrderByDescending(lambada);
break;
default:
throw new NotImplementedException();
break;
}
return result;
}

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