Invoking lambda expressions in Expression trees - c#

I have a SelectionCriteria class that I use for building Entity Framework query expressions, based on PredicateBuilder. Within its limits, it's working fine. I'd like to extend it so that it can query whether a field contains a substring. My problem is that I can't see how to build the needed expression object.
My actual class supports and, or, and not, but they aren't relevant to my question. So I've simplified my example code to handle only a single binary operation:
public class SelectionCriteria
{
public SelectionComparison selectionComparison { get; set; }
public string fieldName { get; set; }
public object fieldValue { get; set; }
public Expression<Func<T, bool>> constructSinglePredicate<T>()
{
var type = typeof(T);
if (type.GetProperty(this.fieldName) == null && type.GetField(this.fieldName) == null)
throw new MissingMemberException(type.Name, this.fieldName);
ExpressionType operation;
if (!operationMap.TryGetValue(this.selectionComparison, out operation))
throw new ArgumentOutOfRangeException("selectionComparison", this.selectionComparison, "Invalid filter operation");
var parameter = Expression.Parameter(type);
var member = Expression.PropertyOrField(parameter, this.fieldName);
var value = (this.fieldValue == null) ? Expression.Constant(null) : Expression.Constant(this.fieldValue, this.fieldValue.GetType());
try
{
var converted = (value.Type != member.Type)
? (Expression) Expression.Convert(value, member.Type)
: (Expression) value;
var comparison = Expression.MakeBinary(operation, member, converted);
var lambda = Expression.Lambda<Func<T, bool>>(comparison, parameter);
return lambda;
}
catch (Exception)
{
throw new InvalidOperationException(
String.Format("Cannot convert value \"{0}\" of type \"{1}\" to field \"{2}\" of type \"{3}\"", this.fieldValue,
value.Type, this.fieldName, member.Type));
}
}
private static Dictionary<SelectionComparison, ExpressionType> operationMap =
new Dictionary<SelectionComparison, ExpressionType>
{
{ SelectionComparison.Equal, ExpressionType.Equal },
{ SelectionComparison.GreaterThan, ExpressionType.GreaterThan },
};
}
public enum SelectionComparison
{
Equal,
GreaterThan,
Contains,
};
Usage is simple:
var criteria = new SelectionCriteria
{
selectionComparison = SelectionComparison.GreaterThan,
fieldName = "worktobegindate",
fieldValue = DateTime.Now.AddDays(-2)
};
var predicate = criteria .constructPredicate<job>();
var jobs = myDbContext.jobs.Where(predicate);
So, my problem - I need a method, like my constructSinglePredictate() above, that returns an Expression> and applies a .Contains(
It's trivial to apply Contains() to a string, given a string to compare it to, but I'm having difficulty figuring out how to do the same in an Expression.
Ideas?

As is usual, I was thinking about things incorrectly. I don't need a lambda that calls a method on a string, I need a MethodExpression (here extracted from the methodMap dictionary):
public Expression<Func<T, bool>> constructMethodCallPredicate<T>()
{
var type = typeof(T);
if (type.GetProperty(this.fieldName) == null && type.GetField(this.fieldName) == null)
throw new MissingMemberException(type.Name, this.fieldName);
MethodInfo method;
if (!methodMap.TryGetValue(this.selectionComparison, out method))
throw new ArgumentOutOfRangeException("selectionComparison", this.selectionComparison, "Invalid filter operation");
var parameter = Expression.Parameter(type);
var member = Expression.PropertyOrField(parameter, this.fieldName);
var value = (this.fieldValue == null) ? Expression.Constant(null) : Expression.Constant(this.fieldValue, this.fieldValue.GetType());
try
{
var converted = (value.Type != member.Type)
? (Expression)Expression.Convert(value, member.Type)
: (Expression)value;
var methodExpression = Expression.Call(member, method, converted);
var lambda = Expression.Lambda<Func<T, bool>>(methodExpression, parameter);
return lambda;
}
catch (Exception)
{
throw new InvalidOperationException(
String.Format("Cannot convert value \"{0}\" of type \"{1}\" to field \"{2}\" of type \"{3}\"", this.fieldValue,
value.Type, this.fieldName, member.Type));
}
}
private static readonly Dictionary<SelectionComparison, MethodInfo> methodMap =
new Dictionary<SelectionComparison, MethodInfo>
{
{ SelectionComparison.Contains, typeof(string).GetMethod("Contains", new[] { typeof(string) }) },
{ SelectionComparison.StartsWith, typeof(string).GetMethod("StartsWith", new[] { typeof(string) }) },
{ SelectionComparison.EndsWith, typeof(string).GetMethod("EndsWith", new[] { typeof(string) }) },
};
public enum SelectionComparison
{
Equal,
NotEqual,
LessThan,
LessThanOrEqual,
GreaterThan,
GreaterThanOrEqual,
Contains,
StartsWith,
EndsWith,
};
Getting an actual "Like" comparison to work, using SqlFunctions.PatIndex is a bit more complicated, PatIndex() returns an int, and I need to wrap it in a >0 expression, but it works just fine, as well.

Related

Variable 'x.Sub' of type 'SubType' referenced from scope '' but it is not defined error

Check this fiddle for the error: https://dotnetfiddle.net/tlz4Qg
I have two classes like this:
public class ParentType{
private ParentType(){}
public int Id { get; protected set; }
public SubType Sub { get; protected set; }
}
public class SubType{
private SubType(){}
public int Id { get; protected set; }
}
I am going to transform a multilevel anonymous expression to a multilevel non-anonymous expression. To achieve this I have an expression like the below-mentioned one:
x => new
{
x.Id,
Sub = new
{
x.Sub.Id
}
}
To achieve that goal, I have transformed it to an expression like this:
x => new ParentType()
{
Id = x.Id,
Sub = new SubType()
{
Id = x.Sub.Id
},
}
But when I call Compile() method, I get the following error:
Variable 'x.Sub' of type 'SubType' referenced from scope '' but it is not defined
Here is my visitor class:
public class ReturnTypeVisitor<TIn, TOut> : ExpressionVisitor
{
private readonly Type funcToReplace;
private ParameterExpression currentParameter;
private ParameterExpression defaultParameter;
private Type currentType;
public ReturnTypeVisitor() => funcToReplace = typeof(Func<,>).MakeGenericType(typeof(TIn), typeof(object));
protected override Expression VisitNew(NewExpression node)
{
if (!node.Type.IsAnonymousType())
return base.VisitNew(node);
if (currentType == null)
currentType = typeof(TOut);
var ctor = currentType.GetPrivateConstructor();
if (ctor == null)
return base.VisitNew(node);
NewExpression expr = Expression.New(ctor);
IEnumerable<MemberBinding> bindings = node.Members.Select(x =>
{
var mi = currentType.GetProperty(x.Name);
//if the type is anonymous then I need to transform its body
if (((PropertyInfo)x).PropertyType.IsAnonymousType())
{
//This section is became unnecessary complex!
//
var property = (PropertyInfo)x;
var parentType = currentType;
var parentParameter = currentParameter;
currentType = currentType.GetProperty(property.Name).PropertyType;
currentParameter = Expression.Parameter(currentType, currentParameter.Name + "." + property.Name);
//I pass the inner anonymous expression to VisitNew and make the non-anonymous expression from it
var xOriginal = VisitNew(node.Arguments.FirstOrDefault(a => a.Type == property.PropertyType) as NewExpression);
currentType = parentType;
currentParameter = parentParameter;
return (MemberBinding)Expression.Bind(mi, xOriginal);
}
else//if type is not anonymous then simple find the property and make the memberbinding
{
var xOriginal = Expression.PropertyOrField(currentParameter, x.Name);
return (MemberBinding)Expression.Bind(mi, xOriginal);
}
});
return Expression.MemberInit(expr, bindings);
}
protected override Expression VisitLambda<T>(Expression<T> node)
{
if (typeof(T) != funcToReplace)
return base.VisitLambda(node);
defaultParameter = node.Parameters.First();
currentParameter = defaultParameter;
var body = Visit(node.Body);
return Expression.Lambda<Func<TIn, TOut>>(body, currentParameter);
}
}
And use it like this:
public static Expression<Func<Tin, Tout>> Transform<Tin, Tout>(this Expression<Func<Tin, object>> source)
{
var visitor = new ReturnTypeVisitor<Tin, Tout>();
var result = (Expression<Func<Tin, Tout>>)visitor.Visit(source);
return result;// result.Compile() throw the aforementioned error
}
Here is the extension methods used inside my Visitor class:
public static ConstructorInfo GetPrivateConstructor(this Type type) =>
type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
// this hack taken from https://stackoverflow.com/a/2483054/4685428
// and https://stackoverflow.com/a/1650895/4685428
public static bool IsAnonymousType(this Type type)
{
var markedWithAttribute = type.GetCustomAttributes(typeof(CompilerGeneratedAttribute), inherit: false).Any();
var typeName = type.Name;
return markedWithAttribute
&& (typeName.StartsWith("<>") || type.Name.StartsWith("VB$"))
&& typeName.Contains("AnonymousType");
}
Update
Here is the .Net Fiddle link for the problem: https://dotnetfiddle.net/tlz4Qg
Update
I have removed the extra codes that seems to be out of the problem scope.
The cause of the problem in question is the line
currentParameter = Expression.Parameter(currentType, currentParameter.Name + "." + property.Name);
inside VisitNew method.
With your sample, it creates a new parameter called "x.Sub", so if we mark the parameters with {}, the actual result is
Sub = new SubType()
{
Id = {x.Sub}.Id
},
rather than expected
Sub = new SubType()
{
Id = {x}.Sub.Id
},
In general you should not create new ParameterExpressions except when remapping lambda expressions. And all newly created parameters should be passed to Expression.Lambda call, otherwise they will be considered "not defined".
Also please note that the visitor code has some assumptions which doesn't hold in general. For instance
var xOriginal = Expression.PropertyOrField(currentParameter, x.Name);
won't work inside nested new, because there you need access to a member of the x parameter like x.Sub.Id rather than x.Id. Which is basically the corersonding expression from NewExpression.Arguments.
Processing nested lambda expressions or collection type members and LINQ methods with expression visitors requires much more state control. While converting simple nested anonymous new expression like in the sample does not even need a ExpressionVisitor, because it could easily be achieved with simple recursive method like this:
public static Expression<Func<Tin, Tout>> Transform<Tin, Tout>(this Expression<Func<Tin, object>> source)
{
return Expression.Lambda<Func<Tin, Tout>>(
Transform(source.Body, typeof(Tout)),
source.Parameters);
}
static Expression Transform(Expression source, Type type)
{
if (source.Type != type && source is NewExpression newExpr && newExpr.Members.Count > 0)
{
return Expression.MemberInit(Expression.New(type), newExpr.Members
.Select(m => type.GetProperty(m.Name))
.Zip(newExpr.Arguments, (m, e) => Expression.Bind(m, Transform(e, m.PropertyType))));
}
return source;
}

Linq orderbys from collection [duplicate]

This question already has answers here:
Dynamic LINQ OrderBy on IEnumerable<T> / IQueryable<T>
(24 answers)
Closed 2 years ago.
How do I specify the argument passed to orderby using a value I take as a parameter?
Ex:
List<Student> existingStudends = new List<Student>{ new Student {...}, new Student {...}}
Currently implementation:
List<Student> orderbyAddress = existingStudends.OrderBy(c => c.Address).ToList();
Instead of c.Address, how can I take that as a parameter?
Example
string param = "City";
List<Student> orderbyAddress = existingStudends.OrderByDescending(c => param).ToList();
You can use a little bit of reflection to construct the expression tree as follows (this is an extension method):
public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string orderByProperty,
bool desc)
{
string command = desc ? "OrderByDescending" : "OrderBy";
var type = typeof(TEntity);
var property = type.GetProperty(orderByProperty);
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var orderByExpression = Expression.Lambda(propertyAccess, parameter);
var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType },
source.Expression, Expression.Quote(orderByExpression));
return source.Provider.CreateQuery<TEntity>(resultExpression);
}
orderByProperty is the Property name you want to order by and if pass true as parameter for desc, will sort in descending order; otherwise, will sort in ascending order.
Now you should be able to do existingStudents.OrderBy("City",true); or existingStudents.OrderBy("City",false);
Here's a possiblity using reflection...
var param = "Address";
var propertyInfo = typeof(Student).GetProperty(param);
var orderByAddress = items.OrderBy(x => propertyInfo.GetValue(x, null));
To expand on the answer by #Icarus: if you want the return type of the extension method to be an IOrderedQueryable instead of an IQueryable, you can simply cast the result as follows:
public static IOrderedQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string orderByProperty, bool desc)
{
string command = desc ? "OrderByDescending" : "OrderBy";
var type = typeof(TEntity);
var property = type.GetProperty(orderByProperty);
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var orderByExpression = Expression.Lambda(propertyAccess, parameter);
var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType },
source.Expression, Expression.Quote(orderByExpression));
return (IOrderedQueryable<TEntity>)source.Provider.CreateQuery<TEntity>(resultExpression);
}
1) Install System.Linq.Dynamic
2) Add the following code
public static class OrderUtils
{
public static string ToStringForOrdering<T, TKey>(this Expression<Func<T, TKey>> expression, bool isDesc = false)
{
var str = expression.Body.ToString();
var param = expression.Parameters.First().Name;
str = str.Replace("Convert(", "(").Replace(param + ".", "");
return str + (isDesc ? " descending" : "");
}
}
3) Write your switch for selecting of Lambda function
public static class SortHelper
{
public static Expression<Func<UserApp, object>> UserApp(string orderProperty)
{
orderProperty = orderProperty?.ToLowerInvariant();
switch (orderProperty)
{
case "firstname":
return x => x.PersonalInfo.FirstName;
case "lastname":
return x => x.PersonalInfo.LastName;
case "fullname":
return x => x.PersonalInfo.FirstName + x.PersonalInfo.LastName;
case "email":
return x => x.Email;
}
}
}
4) Use your helpers
Dbset.OrderBy(SortHelper.UserApp("firstname").ToStringForOrdering())
5) You can use it with pagging (PagedList)
public virtual IPagedList<T> GetPage<TOrder>(Page page, Expression<Func<T, bool>> where, Expression<Func<T, TOrder>> order, bool isDesc = false,
params Expression<Func<T, object>>[] includes)
{
var orderedQueryable = Dbset.OrderBy(order.ToStringForOrdering(isDesc));
var query = orderedQueryable.Where(where).GetPage(page);
query = AppendIncludes(query, includes);
var results = query.ToList();
var total = Dbset.Count(where);
return new StaticPagedList<T>(results, page.PageNumber, page.PageSize, total);
}
Explanation
System.Linq.Dynamic allows us to set string value in OrderBy method. But inside this extension the string will be parsed to Lambda. So I thought it would work if we will parse Lambda to string and give it to OrderBy method. And it works!
Here's something I came up with for dealing with a conditional Descending. You could combine this with other methods of generating the keySelector func dynamically.
public static IOrderedQueryable<TSource> OrderBy<TSource, TKey>(this IQueryable<TSource> source,
System.Linq.Expressions.Expression<Func<TSource, TKey>> keySelector,
System.ComponentModel.ListSortDirection sortOrder
)
{
if (sortOrder == System.ComponentModel.ListSortDirection.Ascending)
return source.OrderBy(keySelector);
else
return source.OrderByDescending(keySelector);
}
Usage:
//imagine this is some parameter
var direction = System.ComponentModel.ListSortDirection.Ascending;
query = query.OrderBy(ec => ec.MyColumnName, direction);
Notice this allows you to chain this .OrderBy extension with a new parameter onto any IQueryable.
// perhaps passed in as a request of user to change sort order
// var direction = System.ComponentModel.ListSortDirection.Ascending;
query = context.Orders
.Where(o => o.Status == OrderStatus.Paid)
.OrderBy(ec => ec.OrderPaidUtc, direction);
private Func<T, object> GetOrderByExpression<T>(string sortColumn)
{
Func<T, object> orderByExpr = null;
if (!String.IsNullOrEmpty(sortColumn))
{
Type sponsorResultType = typeof(T);
if (sponsorResultType.GetProperties().Any(prop => prop.Name == sortColumn))
{
System.Reflection.PropertyInfo pinfo = sponsorResultType.GetProperty(sortColumn);
orderByExpr = (data => pinfo.GetValue(data, null));
}
}
return orderByExpr;
}
public List<T> OrderByDir<T>(IEnumerable<T> source, string dir, Func<T, object> OrderByColumn)
{
return dir.ToUpper() == "ASC" ? source.OrderBy(OrderByColumn).ToList() : source.OrderByDescending(OrderByColumn).ToList();``
}
// Call the code like below
var orderByExpression= GetOrderByExpression<SearchResultsType>(sort);
var data = OrderByDir<SponsorSearchResults>(resultRecords, SortDirectionString, orderByExpression);
This doesn't let you pass a string, as you asked for in your question, but it might still work for you.
The OrderByDescending method takes a Func<TSource, TKey>, so you can rewrite your function this way:
List<Student> QueryStudents<TKey>(Func<Student, TKey> orderBy)
{
return existingStudents.OrderByDescending(orderBy).ToList();
}
There are other overloads for OrderByDescending as well that take a Expression<Func<TSource, TKey>>, and/or a IComparer<TKey>. You could also look into those and see if they provide you anything of use.
The only solution that worked for me was posted here https://gist.github.com/neoGeneva/1878868 by neoGeneva.
I will re-post his code because it works well and I wouldn't want it to be lost in the interwebs!
public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string sortExpression)
{
if (source == null)
throw new ArgumentNullException("source", "source is null.");
if (string.IsNullOrEmpty(sortExpression))
throw new ArgumentException("sortExpression is null or empty.", "sortExpression");
var parts = sortExpression.Split(' ');
var isDescending = false;
var propertyName = "";
var tType = typeof(T);
if (parts.Length > 0 && parts[0] != "")
{
propertyName = parts[0];
if (parts.Length > 1)
{
isDescending = parts[1].ToLower().Contains("esc");
}
PropertyInfo prop = tType.GetProperty(propertyName);
if (prop == null)
{
throw new ArgumentException(string.Format("No property '{0}' on type '{1}'", propertyName, tType.Name));
}
var funcType = typeof(Func<,>)
.MakeGenericType(tType, prop.PropertyType);
var lambdaBuilder = typeof(Expression)
.GetMethods()
.First(x => x.Name == "Lambda" && x.ContainsGenericParameters && x.GetParameters().Length == 2)
.MakeGenericMethod(funcType);
var parameter = Expression.Parameter(tType);
var propExpress = Expression.Property(parameter, prop);
var sortLambda = lambdaBuilder
.Invoke(null, new object[] { propExpress, new ParameterExpression[] { parameter } });
var sorter = typeof(Queryable)
.GetMethods()
.FirstOrDefault(x => x.Name == (isDescending ? "OrderByDescending" : "OrderBy") && x.GetParameters().Length == 2)
.MakeGenericMethod(new[] { tType, prop.PropertyType });
return (IQueryable<T>)sorter
.Invoke(null, new object[] { source, sortLambda });
}
return source;
}
Add the nugget package Dynamite to your code
Add the namespace Dynamite.Extensions
Eg : using Dynamite.Extensions;
Give Order by query like any SQL query
Eg : students.OrderBy(" City DESC, Address").ToList();
To extend the response of #Icarus: if you want to sort by two fields I could perform the following function (for one field the response of Icarius works very well).
public static IQueryable<T> OrderByDynamic<T>(this IQueryable<T> q, string SortField1, string SortField2, bool Ascending)
{
var param = Expression.Parameter(typeof(T), "p");
var body = GetBodyExp(SortField1, SortField2, param);
var exp = Expression.Lambda(body, param);
string method = Ascending ? "OrderBy" : "OrderByDescending";
Type[] types = new Type[] { q.ElementType, exp.Body.Type };
var mce = Expression.Call(typeof(Queryable), method, types, q.Expression, exp);
return q.Provider.CreateQuery<T>(mce);
}
This is the function that the body returns for the lambda expression, it works with string and int, but it is enough to add more types to make it work according to the need of each programmer
public static NewExpression GetBodyExp(string field1, string field2, ParameterExpression Parametro)
{
// SE OBTIENE LOS NOMBRES DE LOS TIPOS DE VARIABLE
string TypeName1 = Expression.Property(Parametro, field1).Type.Name;
string TypeName2 = Expression.Property(Parametro, field2).Type.Name;
// SE DECLARA EL TIPO ANONIMO SEGUN LOS TIPOS DE VARIABLES
Type TypeAnonymous = null;
if (TypeName1 == "String")
{
string var1 = "0";
if (TypeName2 == "Int32")
{
int var2 = 0;
var example = new { var1, var2 };
TypeAnonymous = example.GetType();
}
if (TypeName2 == "String")
{
string var2 = "0";
var example = new { var1, var2 };
TypeAnonymous = example.GetType();
}
}
if (TypeName1 == "Int32")
{
int var1 = 0;
if (TypeName2 == "Int32")
{
string var2 = "0";
var example = new { var1, var2 };
TypeAnonymous = example.GetType();
}
if (TypeName2 == "String")
{
string var2 = "0";
var example = new { var1, var2 };
TypeAnonymous = example.GetType();
}
}
//se declaran los TIPOS NECESARIOS PARA GENERAR EL BODY DE LA EXPRESION LAMBDA
MemberExpression[] args = new[] { Expression.PropertyOrField(Parametro, field1), Expression.PropertyOrField(Parametro, field2) };
ConstructorInfo CInfo = TypeAnonymous.GetConstructors()[0];
IEnumerable<MemberInfo> a = TypeAnonymous.GetMembers().Where(m => m.MemberType == MemberTypes.Property);
//BODY
NewExpression body = Expression.New(CInfo, args, TypeAnonymous.GetMembers().Where(m => m.MemberType == MemberTypes.Property));
return body;
}
to use it the following is done
IQueryable<MyClass> IqMyClass= context.MyClass.AsQueryable();
List<MyClass> ListMyClass= IqMyClass.OrderByDynamic("UserName", "IdMyClass", true).ToList();
if there is a better way to do this, it would be great if they share it
I managed to solve it thanks to: How can I make a Multiple property lambda expression with Linq
New Answer : this is a more complete answer that supports multiple columns for order by like SQL. Example : .OrderBy("FirstName,Age DESC") :
namespace Utility;
public static class QueryExtension
{
public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string orderByProperty, bool desc, bool isThenBy = false)
{
string command = isThenBy ? (desc ? "ThenByDescending" : "ThenBy") : (desc ? "OrderByDescending" : "OrderBy");
var type = typeof(TEntity);
var property = type.GetProperty(orderByProperty);
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var orderByExpression = Expression.Lambda(propertyAccess, parameter);
var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType },
source.Expression, Expression.Quote(orderByExpression));
return source.Provider.CreateQuery<TEntity>(resultExpression);
}
public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string sqlOrderByList)
{
var ordebyItems = sqlOrderByList.Trim().Split(',');
IQueryable<TEntity> result = source;
bool useThenBy = false;
foreach (var item in ordebyItems)
{
var splt = item.Trim().Split(' ');
result = result.OrderBy(splt[0].Trim(), (splt.Length > 1 && splt[1].Trim().ToLower() == "desc"), useThenBy);
if (useThenBy)
useThenBy = true;
}
return result;
}
}
The second function iterates over orderby columns and uses the first one.
Use it like this :
using Utility;
...
public void MyMethod()
{
var query = _dbContext.Person.AsQueryable();
query.OrderBy("FirstName,Age DESC");
}
I'm way late to the party but none of these solutions worked for me. I was eager to try System.Linq.Dynamic, but I couldn't find that on Nuget, maybe depreciated? Either way...
Here is a solutions I came up with. I needed to dynamically use a mixture of OrderBy, OrderByDescending and OrderBy > ThenBy.
I simply created an extension method for my list object, a bit hacky I know... I wouldn't recommend this if it were something I was doing a lot of, but it's good for a one off.
List<Employee> Employees = GetAllEmployees();
foreach(Employee oEmployee in Employees.ApplyDynamicSort(eEmployeeSort))
{
//do stuff
}
public static IOrderedEnumerable<Employee> ApplyDynamicSort(this List<Employee> lEmployees, Enums.EmployeeSort eEmployeeSort)
{
switch (eEmployeeSort)
{
case Enums.EmployeeSort.Name_ASC:
return lEmployees.OrderBy(x => x.Name);
case Enums.EmployeeSort.Name_DESC:
return lEmployees.OrderByDescending(x => x.Name);
case Enums.EmployeeSort.Department_ASC_Salary_DESC:
return lEmployees.OrderBy(x => x.Department).ThenByDescending(y => y.Salary);
default:
return lEmployees.OrderBy(x => x.Name);
}
}

Handle null values of nested objects in C# expression tree

I have searched, and found similar posts pertaining to my issue, however nothing seems to solve my problem.
I am fairly new to C#, and this is my first attempt at building an expression tree. (please go easy ;-)
I am trying to create an expression tree which would, once compiled, filter values on a set of data.
Here is my expression method:
private static Expression<Func<TItem, bool>> CreateFilterExpression<TItem>(string propertyName, string expressionType, dynamic filterValue)
{
if (param == null)
{
param = Expression.Parameter(typeof(TItem), "item");
}
MemberExpression member = GetMemberExpression<TItem>(propertyName);
//When we call our method, we need to evaluate on the same type
//we convert the filter value to the type of the property we are evaluating on
dynamic convertedValue = Convert.ChangeType(filterValue, member.Type);
MethodInfo method = member.Type.GetMethod(expressionType, new[] { member.Type });
ConstantExpression constantValue = Expression.Constant(convertedValue, member.Type);
Expression containsMethodExp;
if (expressionType == "NotEqual")
{
method = member.Type.GetMethod("Equals", new[] { member.Type });
}
if (member.Type.ToString().ToLower() == "system.string")
{
//We need to compare the lower case of property and value
MethodCallExpression propertyValueToLowerCase = Expression.Call(member, typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));
MethodCallExpression filterValueToLowerCase = Expression.Call(constantValue, typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));
containsMethodExp = Expression.Call(propertyValueToLowerCase, method, filterValueToLowerCase);
}
else if (member.Type.ToString().ToLower() == "system.datetime")
{
//we need to compare only the dates
MemberExpression dateOnlyProperty = Expression.Property(member, "Date");
containsMethodExp = Expression.Call(dateOnlyProperty, method, constantValue);
}
else
{
containsMethodExp = Expression.Call(member, method, constantValue);
}
if (expressionType == "NotEqual")
{
containsMethodExp = Expression.Not(containsMethodExp);
}
return Expression.Lambda<Func<TItem, bool>>(containsMethodExp, param);
}
private static MemberExpression GetMemberExpression<TItem>(string propertyName)
{
if (param == null)
{
param = Expression.Parameter(typeof(TItem), "item");
}
MemberExpression member = null;
//Check if we have a nested property
if (propertyName.Contains('.'))
{
Expression nestedProperty = param;
string[] properies = propertyName.Split('.');
int zeroIndex = properies.Count() - 1;
for (int i = 0; i <= zeroIndex; i++)
{
if (i < zeroIndex)
{
nestedProperty = Expression.PropertyOrField(nestedProperty, properies[i]);
}
else
{
member = Expression.Property(nestedProperty, properies[i]);
}
}
}
else
{
member = Expression.Property(param, propertyName);
}
return member;
}
Example usage would be like so:
var lambda = CreateFilterExpression<T>("Some.Nested.Object", "Equals", "Some value");
var compiled = lambda.Compile();
gridData = gridData.Where(compiled);
An example of the data I trying to ultimately bind to my grid looks like this:
public class Some : BaseClass
{
public decimal NumberAvailable { get; set; }
public DateTime EffectiveDate { get; set; }
public Batch Batch { get; set; }
public decimal Price { get; set; }
public decimal Limit { get; set; }
public NestedClass Nested { get; set; }
public int? CompanyId { get; set; }
public decimal Amount { get; set; }
}
public class NestedClass : BaseClass
{
public int RequestId { get; set; }
public string Code { get; set; }
public string Company { get; set; }
public string Reference { get; set; }
}
The problem occurs when we have null value on an object, like "Some.Nested = null", and then trying to convert "Reference" to lowercase. Here:
MethodCallExpression propertyValueToLowerCase = Expression.Call(member, typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));
Here is the result in debugger:
How can I check for null values, on nested objects, and return empty string if it is null?
I hope I explained my question well enough. Thank you in advance!
What you want to do is to generate an expression like this:
Some == null ? null : Some.Nested == null ? null : Some.Nested.Object
This unfortunately is no longer a member expression, so GetMemberExpression wouldn’t work for this. Instead you need a chain of conditional expression that accesses one more level at a time.
Once you have that, you could then do <memberExpression> ?? string.Empty to get a string which you can safely operate on.
To generate the latter expression, you can use Expression.Coalesce:
Expression.Coalesce(memberExpression, Expression.Constant(string.Empty))
For the member expression itself, you could write something like this:
Expression AccessMember(Expression obj, string propertyName)
{
string[] parts = propertyName.Split(new char[] { '.' }, 2);
Expression member = Expression.PropertyOrField(obj, parts[0]);
if (parts.Length > 1)
member = AccessMember(member, parts[1]);
return Expression.Condition(Expression.Equal(obj, Expression.Constant(null)),
Expression.Constant(null, member.Type), member);
}
This can be used like this:
string path = "Some.Nested.Object";
string[] parts = path.Split(new char[] { '.' }, 2);
ParameterExpression param = Expression.Parameter(typeof(T), parts[0]);
Expression memberAccess = AccessMember(param, parts[1]);
memberAccess would then be exactly the above chained conditional expression.
Combined into your function (simplified only for strings for now), it could look like this:
Expression<Func<TObj, bool>> BuildFilterExpression<TObj, TMember>(string propertyPath, TMember comparisonValue, TMember defaultValue)
{
string[] parts = propertyPath.Split(new char[] { '.' }, 2);
ParameterExpression param = Expression.Parameter(typeof(TObj), parts[0]);
// get member access expression
Expression memberExpression = AccessMember(param, parts[1]);
// coalesce the member with the default value
memberExpression = Expression.Coalesce(memberExpression, Expression.Constant(defaultValue));
// get the comparison value as expression
Expression comparisonExpression = Expression.Constant(comparisonValue);
// type specific logic
if (memberExpression.Type == typeof(string))
{
MethodInfo toLowerMethod = typeof(string).GetMethod("ToLower", Type.EmptyTypes);
memberExpression = Expression.Call(memberExpression, toLowerMethod);
comparisonExpression = Expression.Call(comparisonExpression, toLowerMethod);
}
// create the comparison expression
Expression filterExpression = Expression.Equal(memberExpression, comparisonExpression);
return Expression.Lambda<Func<TObj, bool>>(filterExpression, param);
}
Used like this:
BuildFilterExpression<SomeType, string>("Some.Nested.Object", "foo bar", string.Empty)
… it essentially creates the following lambda expression:
(Some) => ((Some == null ? null : Some.Nested == null ? null : Some.Nested.Object) ?? string.Empty).ToLower() == "foo bar"
Above code assumes that for a property expression Some.Nested.Object, Some is the object that is being passed to the lambda, so the first property that would be accessed is Nested. The reason is that I simply didn’t know your example object structure, so I had to come up with something.
If you want Some be the first property that is accessed for the passed object, you can easily change that though. To do that, modify the beginning of BuildFilterExpression so that the propertyPath is not split up. Pass some random name (or no name even) to Expression.Parameter, and pass the full propertyPath to AccessMember:
// don’t split up the propertyPath
// let’s call the parameter `obj`
ParameterExpression param = Expression.Parameter(typeof(TObj), "obj");
// get member access expression—for the full property path
Expression memberExpression = AccessMember(param, propertyPath);

How to get Expression for Nullable values ( fields ) without converting from Expression.Convert in C#?

I am dealing with the scanario of Nullable types during formation of dynamic query expressions. These expressions would fetch filtered data from any SQL Tables( interfacing with Code First classes using EF ).
I have normal object ( e.g Consignment operating on several properties along with Nullable properties).
My expression formation goes well untill I encounter some Nullable types. On these nullables, I am getting
The binary operator NotEqual is not defined for the types 'System.Nullable`1[System.Single]' and 'System.Single'.
For removing this exception, I am using all appraoches regarding convertion posted on different threads.
Invoking lambda expressions in Expression trees
Trying to filter on a Nullable type using Expression Trees
These all are generating expressions with added word "Convert" ( i.e Convert(someValue) ) and in result I always have expression
t=>(t.Consignment.Id = 45000 && t.Consignment.someProperty>=45 Or t.Consignment.Weight! = Convert(5000)).
Of course I need the whole above expression WITHOUT "Convert". Because this "Convert" will not fetch the data from tables accordingly.
Any help would be greatly appreciated! What should left to do? I already know conversion, but this makes the whole expression useless, because it won't project the records because of needless "Convert"
Added
Expression NotEqual<T>(Expression PropertyType, ConstantExpression a_Constant, ParameterExpression parameter)
{
if(IsNullableType(Property.Type) &&!IsNullableType(a_Constant.Type))
{
var converted = a_Constant.Type != Property.Type ? (Expression)Expression.Convert(a_Constant, Property.Type): (Expression)a_Constant;
// here above statement returns (Convert(50000)) and all I want (50000), but i tried all combinitions from Expression in order to form this constant as expression, it always throws exception what I mentioned originally.
var body = Expression.MakeBinary(ExpressionType.NotEqual, PropertyType, converted);
//MakeBinary statement returns {(t.Weight != Convert(5000000))} but I need {(t.Weight != 5000000)}
var expr = Expression.Lambda<Func<T, bool>>(body, parameter);
return expr;
}
}
Code:
public class Consignment
{
public float? Weight { get; set; }
}
public static class GenericQueryExpressionBuilder
{
private static Expression NotEqual<T>(Expression memberExpression, ConstantExpression a_Constant, ParameterExpression parameter)
{
ConstantExpression constantExpression = null;
if (IsNullableType(memberExpression.Type) && !IsNullableType(a_Constant.Type))
{
//var converted = a_Constant.Type != memberExpression.Type ? (Expression)Expression.Convert(a_Constant, memberExpression.Type) : (Expression)a_Constant;
Expression constantExp = Expression.Property(a_Constant,typeof(T),"Weight");
**// above statement throws exception I commented.**
var body = Expression.MakeBinary(ExpressionType.NotEqual, memberExpression, converted);
//here I want "t=>(t.Weight!=5000.0) INSTEAD of t=>(t.Weight!=Convert(5000.0))"
var expr = Expression.Lambda<Func<T, bool>>(body, parameter);
return expr;
}
else if (!IsNullableType(memberExpression.Type) && IsNullableType(a_Constant.Type))
memberExpression = Expression.Convert(memberExpression, a_Constant.Type);
return Expression.NotEqual(memberExpression, constantExpression);
}
static bool IsNullableType(Type t)
{
return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>);
}
private static Expression GetExpression<T>(ParameterExpression param, string a_strPropertyName, string Operator, object Value)
{
MemberExpression member = Expression.Property(param, a_strPropertyName);
ConstantExpression constant = Expression.Constant(Value);
try
{
return GenericQueryExpressionBuilder.NotEqual<T>(member, constant, param);
}
catch (InvalidOperationException)
{
return null;
}
return null;
}
public static Expression<Func<T, bool>> GetExpression<T>(Consignment consignment)
{
Expression expression = null;
var parameter = Expression.Parameter(typeof(T), "t");
string PropertyName = "Weight";
string Operation = "NotEqual";
object Value = consignment.Weight;
expression = GenericQueryExpressionBuilder.GetExpression<T>(parameter, PropertyName, Operation, Value);
return Expression.Lambda<Func<T, bool>>(expression, parameter);
}
}
class Program
{
static void Main(string[] args)
{
Consignment consignment = new Consignment();
consignment.Weight = 50000.0f;
var deleg = GenericQueryExpressionBuilder.GetExpression<Consignment>(consignment).Compile();
}
}
Here's a short but complete example showing how to build the c => c.Weight.HasValue && c.Weight.Value != 5000f expression tree. I've removed a lot of irrelevant code from the question:
using System;
using System.Linq.Expressions;
public class Consignment
{
public float? Weight { get; set; }
}
public class Test
{
private static Expression NotEqual(Expression memberExpression,
ConstantExpression constantToCompare)
{
// Other cases removed, for simplicity. This answer only demonstrates
// how to handle c => c.Weight != 5000f.
var hasValueExpression = Expression.Property(memberExpression, "HasValue");
var valueExpression = Expression.Property(memberExpression, "Value");
var notEqual = Expression.NotEqual(valueExpression, constantToCompare);
return Expression.AndAlso(hasValueExpression, notEqual);
}
static void Main(string[] args)
{
Consignment consignment = new Consignment();
consignment.Weight = 50000.0f;
var parameter = Expression.Parameter(typeof(Consignment), "c");
var weight = Expression.Property(parameter, "Weight");
var constant = Expression.Constant(5000f, typeof(float));
var weightNotEqualExpression = NotEqual(weight, constant);
var lambda = Expression.Lambda<Func<Consignment, bool>>
(weightNotEqualExpression, parameter);
Console.WriteLine(lambda);
}
}

LINQ to Entities - Addressing class properties with their string names

I have a Kendo grid that has serverside filtering turned on. The field to filter by is passed as a string. For example, I want to filter by "SampleId". Now, I need to write a LINQ to Entities query that can filter by using the strongly-typed property SampleId. For example:
db.Cases.Where(x=>targetlist.Contains(x.SampleId))
where targetlist is a list of items from the filter.
So, in effect, is there a way to write a query such that "SampleId" can directly translate to Case.SampleId?
I have tried reflection and made use of the GetProperty and GetValue and LINQ to Entities does not like it.
Any suggestions will be much appreciated!
EDIT (by pid for original poster srinaik2020):
#zaitsman: this is the code posted in an comment further below and is the actual resolution of the problem, based on the accepted answer.
public static class MyExtensions
{
public static string GetPropertyByName(this CaseV case1, string name)
{
var x = typeof (CaseV).GetProperty(name).GetValue(case1);
if (x != null)
{
return x.ToString();
} else {
return "none";
}
}
}
You can use an extension method and attach it to the class.
That method should use reflection to retrieve the property from the object.
Here are the pointers:
Extension methods
GetProperty()
Once this is working, you'll want just to set and/or get the value of the property.
The GetProperty() method above just returns the PropertyInfo object.
To get or set the value you'll have to use the appropriate methods of PropertyInfo.
I'd not expose the PropertyInfo because it would ruin the magic.
Better have to extension methods, then:
T GetPropertyByName<T>(string name);
SetPropertyByName<T>(string name, T value);
I didn't think any of these answered actually how to do the getproperty so included my working code after further research. mymodelclass is the class of the entity/model used to MyFilteredData.
var srchItem1 = typeof(mymodelclass).GetProperty("Name");
var srchItem2 = typeof(mymodelclass).GetProperty("Description");
var srchItem3 = typeof(mymodelclass).GetProperty("LongDescription");
if (MySearchText != null && srchItem1 != null)
{
if (srchItem2 == null) { srchItem2 = srchItem1; }
if (srchItem3 == null) { srchItem3 = srchItem1; }
MyFilteredData = MyFilteredData.
Where(c => srchItem1.GetValue(c).ToString().ToLower().Contains(MySearchText.ToLower()) ||
srchItem2.GetValue(c).ToString().ToLower().Contains(MySearchText.ToLower()) ||
srchItem3.GetValue(c).ToString().ToLower().Contains(MySearchText.ToLower())
);
}
if you like expressions, you can use them to get the value of a property like so (taken from some helpers i have made in LinqPad so it might not be full code):
public static class Helper {
public static IEnumerable<T> Select<T>( this IEnumerable enumerable, string memberName ) {
IQueryable queryable = enumerable.AsQueryable();
LambdaExpression expression = PredicateFor( queryable.ElementType, memberName );
return CreateQuery( queryable, "Select", new[] { expression.ReturnType }, expression ).Cast<T>();
}
public static MemberExpression NestedPropertyOrField(this Expression expression, string nestedPropertyOrFieldName) {
MemberExpression e;
if (nestedPropertyOrFieldName.IndexOf('.') >= 0) {
var split = nestedPropertyOrFieldName.Split(new[] { '.' }, 2, StringSplitOptions.RemoveEmptyEntries);
if (split.Length > 0) {
e = Expression.PropertyOrField(expression, split[0]);
if (split.Length > 1) {
e = NestedPropertyOrField(e, split[1]);
}
} else {
throw new ArgumentException("'" + nestedPropertyOrFieldName + "' is not a member of type '" + expression.Type.AssemblyQualifiedName + "'");
}
} else {
e = Expression.PropertyOrField(expression, nestedPropertyOrFieldName);
}
return e;
}
private static IEnumerable CreateQuery( IEnumerable enumerable, string method, Type[] typeArguments, params Expression[] arguments ) {
IQueryable queryable = enumerable.AsQueryable();
Type[] typeArgs = new[] { queryable.ElementType }.Concat( typeArguments ?? new Type[ 0 ] ).ToArray();
Expression[] args = new[] { queryable.Expression }.Concat( arguments ?? new Expression[ 0 ] ).ToArray();
MethodCallExpression methodCallExpression = Expression.Call( typeof( Queryable ), method, typeArgs, args );
return queryable.Provider.CreateQuery( methodCallExpression );
}
internal static LambdaExpression PredicateFor( Type elementType, string memberName ) {
var pe = Expression.Parameter( elementType, "#item" );
Expression expression = pe;
if ( memberName.StartsWith( "#item", StringComparison.OrdinalIgnoreCase ) ) {
memberName = memberName.Substring( 5 );
}
if ( memberName.Length > 0 )
expression = NestedPropertyOrField( expression, memberName );
var delegateType = Expression.GetFuncType( elementType, expression.Type );
return Expression.Lambda( delegateType, expression, new[] {pe} );
}
}
and then do
string propertyName = // get property name from somewhere, ie: "SomeObject.NestedProperty.ID"
db.Cases.Select<string>(propertyName).Where(targetlist.Contains);

Categories

Resources