Lambda Expression for dynamic Object - c#

I am trying to build a Lambda Expression for a table that has been created at run time.
The Expression is build fine but when I call Compile() method I get this error
"ParameterExpression of type 'cseval.Item' cannot be used for delegate parameter of type 'System.Object'"
this is my function
public Func<dynamic, Boolean> GetWhereExp(List<WhereCondition> SearchFieldList, dynamic item)
{
ParameterExpression pe = Expression.Parameter(item.GetType(), "c");
Expression combined = null;
if (SearchFieldList != null)
{
foreach (WhereCondition fieldItem in SearchFieldList)
{
//Expression for accessing Fields name property
Expression columnNameProperty = Expression.Property(pe, fieldItem.ColumName);
//the name constant to match
Expression columnValue = Expression.Constant(fieldItem.Value);
//the first expression: PatientantLastName = ?
Expression e1 = Expression.Equal(columnNameProperty, columnValue);
if (combined == null)
{
combined = e;
}
else
{
combined = Expression.And(combined, e);
}
}
}
var result = Expression.Lambda<Func<dynamic, bool>>(combined, pe);
return result.Compile();
}

I've changed dynamic to generics, this code works for me:
public Func<T, Boolean> GetWhereExp<T>(List<WhereCondition> SearchFieldList, T item)
{
var pe = Expression.Parameter(item.GetType(), "c");
Expression combined = null;
if (SearchFieldList != null)
{
foreach (var fieldItem in SearchFieldList)
{
var columnNameProperty = Expression.Property(pe, fieldItem.ColumName);
var columnValue = Expression.Constant(fieldItem.Value);
var e1 = Expression.Equal(columnNameProperty, columnValue);
combined = combined == null ? e1 : Expression.And(combined, e1);
}
}
var result = Expression.Lambda<Func<T, bool>>(combined, pe);
return result.Compile();
}
Small remark: your method returns function, not an expression, so the name 'GetWhereExp' is slightly incorrect. If you want to return function, imho, it's better to use reflection.
UPD: I use this code to test:
var expressions = new List<WhereCondition>
{
new WhereCondition("Column1", "xxx"),
new WhereCondition("Column2", "yyy"),
};
var item = new
{
Column1 = "xxx",
Column2 = "yyy"
};
var func = LinqExpr.GetWhereExp(expressions, (dynamic)item);
Console.WriteLine(new[] {item}.Count(a => func(a)));

Related

Add variable amount of conditions to expression in loop C#

var row = parser.ReadFields();
Expression<Func<DataRow, bool>> expression = null;
foreach (var pairToCheck in mappingDictionary)
{
Expression<Func<DataRow, bool>> newCondition = r => r[pairToCheck.Value].ToString() == row[pairToCheck.Key];
if (expression == null)
expression = newCondition;
else
expression = Expression.And(expression, newCondition.Body); // Compile error, I can't do this apparently
}
var recordFound = dt.AsEnumerable().Where(expression.Compile()).Count() > 0;
See code above, I'm trying to find a record in a DataTable (var dt), but the amount of conditions that this record has to satisfy, is variable. I tried to add conditions with Expression.And, but it turns the expression into type BinaryExpression and I can no longer turn it back to the original Expression<Func<DataRow, bool>>. What am I missing here? Is my approach at least correct?
You need to have a little bit more complicated handling. Something like this:
Expression expression = null;
var parameter = Expression.Parameter(typeof(DataRow)); // create a parameter for your future lambda
foreach (var pairToCheck in mappingDictionary)
{
Expression<Func<DataRow, bool>> newCondition = r => r[pairToCheck.Value].ToString() == row[pairToCheck.Key];
// build new body replacing parameter to correct one
var newBody = newCondition.Body.ReplaceParameter(newCondition.Parameters.First(), parameter);
if (expression == null)
expression = newBody;
else
expression = Expression.AndAlso(expression, newBody); // use AndAlso, And is a bitwise AND operation
}
var result = Expression.Lambda<Func<DataRow, bool>>(expression, parameter); // construct lambda
var recordFound = dt.AsEnumerable().Where(result.Compile()).Count() > 0;
And the ReplaceParameter method comes form:
public static class ExpressionExt
{
public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
{
return new ParameterReplacingVisitor { Source = source, Target = target }.Visit(expression);
}
class ParameterReplacingVisitor : ExpressionVisitor
{
public ParameterExpression Source;
public Expression Target;
protected override Expression VisitParameter(ParameterExpression node)
{
return node == Source ? Target : base.VisitParameter(node);
}
}
}

C# Expression, Accessing Property

I am trying to build a filter method for IQueryable Type,
I could achieve this for ex:
query = query.Filter(UsersListId, e => e.UserId);
public static IQueryable<T> Filter<T, TSearch>(this IQueryable<T> query, List<TSearch> list, Expression<Func<T, TSearch>> props)
{
if (list == null || list.Count == 0)
{
return query;
}
var propertyPath = props.Body.ToString().Replace(props.Parameters[0] + ".", string.Empty);
var containsMethod = typeof(List<TSearch>).GetMethod("Contains", new Type[] { typeof(TSearch) });
ConstantExpression constlist = Expression.Constant(list);
var param = Expression.Parameter(typeof(T), "Entity");
var newvalue = GetPropertyValue(param, propertyPath);
var body = Expression.Call(constlist, containsMethod, newvalue);
var exp = Expression.Lambda<Func<T, bool>>(body, param);
return query.Where(exp);
}
public static MemberExpression GetPropertyValue(ParameterExpression parExp, string propertyPath)
{
var properties = propertyPath.Split('.').ToArray();
var value = Expression.Property(parExp, properties[0]);
for (int i = 1; i < properties.Length; i++)
{
value = Expression.Property(value, properties[i]);
}
return value;
}
this code takes the propertyPath (ex: x.User.Department.Id)
and returns the value,
The problems here:
I cannot pass a nullable object if the list isn't nullable too
I am not quite sure weather it's the right way to access properties
so my question is what is the solution for these problems?

string expression to c# function delegate

I want to convert the following string into function delegate.
[Id]-[Description]
C# class:
public class Foo
{
public string Id {get;set;}
public string Description {get;set;}
}
Result function delegate:
Func<Foo, string> GetExpression = delegate()
{
return x => string.Format("{0}-{1}", x.Id, x.Description);
};
I think compiled lambda or expression parser would be a way here, but not sure about the best way much. Any inputs?
It's possible as: to construct Linq Expression then compile it. Compiled expression is an ordinary delegate, with no performance drawbacks.
An example of implementation if type of argument(Foo) is known at compile time:
class ParserCompiler
{
private static (string format, IReadOnlyCollection<string> propertyNames) Parse(string text)
{
var regex = new Regex(#"(.*?)\[(.+?)\](.*)");
var formatTemplate = new StringBuilder();
var propertyNames = new List<string>();
var restOfText = text;
Match match;
while ((match = regex.Match(restOfText)).Success)
{
formatTemplate.Append(match.Groups[1].Value);
formatTemplate.Append("{");
formatTemplate.Append(propertyNames.Count);
formatTemplate.Append("}");
propertyNames.Add(match.Groups[2].Value);
restOfText = match.Groups[3].Value;
}
formatTemplate.Append(restOfText);
return (formatTemplate.ToString(), propertyNames);
}
public static Func<T, string> GetExpression<T>(string text) //"[Id]-[Description]"
{
var parsed = Parse(text); //"{0}-{1} Id, Description"
var argumentExpression = Expression.Parameter(typeof(T));
var properties = typeof(T)
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetField)
.ToDictionary(keySelector: propInfo => propInfo.Name);
var formatParamsArrayExpr = Expression.NewArrayInit(
typeof(object),
parsed.propertyNames.Select(propName => Expression.Property(argumentExpression, properties[propName])));
var formatStaticMethod = typeof(string).GetMethod("Format", BindingFlags.Static | BindingFlags.Public, null,new[] { typeof(string), typeof(object[]) }, null);
var formatExpr = Expression.Call(
formatStaticMethod,
Expression.Constant(parsed.format, typeof(string)),
formatParamsArrayExpr);
var resultExpr = Expression.Lambda<Func<T, string>>(
formatExpr,
argumentExpression); // Expression<Func<Foo, string>> a = (Foo x) => string.Format("{0}-{1}", x.Id, x.Description);
return resultExpr.Compile();
}
}
And usage:
var func = ParserCompiler.GetExpression<Foo>("[Id]-[Description]");
var formattedString = func(new Foo {Id = "id1", Description = "desc1"});
An almost identical answer was posted while I was testing this, but, as the below code has an advantage of calling each property mentioned in the formatting string at most once, I'm posting it anyway:
public static Func<Foo, string> GetExpression(string query_string)
{
(string format_string, List<string> prop_names) = QueryStringToFormatString(query_string);
var lambda_parameter = Expression.Parameter(typeof(Foo));
Expression[] formatting_params = prop_names.Select(
p => Expression.MakeMemberAccess(lambda_parameter, typeof(Foo).GetProperty(p))
).ToArray();
var formatMethod = typeof(string).GetMethod("Format", new[] { typeof(string), typeof(object[]) });
var format_call = Expression.Call(formatMethod, Expression.Constant(format_string), Expression.NewArrayInit(typeof(object), formatting_params));
var lambda = Expression.Lambda(format_call, lambda_parameter) as Expression<Func<Foo, string>>;
return lambda.Compile();
}
// A *very* primitive parser, improve as needed
private static (string format_string, List<string> ordered_prop_names) QueryStringToFormatString(string query_string)
{
List<string> prop_names = new List<string>();
string format_string = Regex.Replace(query_string, #"\[.+?\]", m => {
string prop_name = m.Value.Substring(1, m.Value.Length - 2);
var known_pos = prop_names.IndexOf(prop_name);
if (known_pos < 0)
{
prop_names.Add(prop_name);
known_pos = prop_names.Count - 1;
}
return $"{{{known_pos}}}";
});
return (format_string, prop_names);
}
The inspiration comes from Generate lambda Expression By Clause using string.format in C#?.
A simple step by step version to create an Expression tree based on simple use case, can help in creating any kind of Expression tree
What we want to Achieve: (coding in linqpad, Dump is a print call)
Expression<Func<Foo,string>> expression = (f) => string.Format($"{f.Id}-
{f.Description}");
var foo = new Foo{Id = "1",Description="Test"};
var func = expression.Compile();
func(foo).Dump(); // Result "1-Test"
expression.Dump();
Following is the Expression generated:
Step by Step process to Create an Expression Tree
On Reviewing the Expression Tree, following points can be understood:
We create a Func delegate of type typeof(Func<Foo,String>)
Outer Node Type for Expression is Lambda Type
Just needs one parameter Expression of typeof(Foo)
In Arguments it needs, MethodInfo of string.Format
In arguments to Format method, it needs following Expressions
a.) Constant Expression - {0}-{1}
b.) MemberExpression for Id field
c.) MemberExpression for Description field
Viola and we are done
Using the Steps above following is the simple code to create Expression:
// Create a ParameterExpression
var parameterExpression = Expression.Parameter(typeof(Foo),"f");
// Create a Constant Expression
var formatConstant = Expression.Constant("{0}-{1}");
// Id MemberExpression
var idMemberAccess = Expression.MakeMemberAccess(parameterExpression, typeof(Foo).GetProperty("Id"));
// Description MemberExpression
var descriptionMemberAccess = Expression.MakeMemberAccess(parameterExpression, typeof(Foo).GetProperty("Description"));
// String.Format (MethodCallExpression)
var formatMethod = Expression.Call(typeof(string),"Format",null,formatConstant,idMemberAccess,descriptionMemberAccess);
// Create Lambda Expression
var lambda = Expression.Lambda<Func<Foo,string>>(formatMethod,parameterExpression);
// Create Func delegate via Compilation
var func = lambda.Compile();
// Execute Delegate
func(foo).Dump(); // Result "1-Test"

LINQ Expression Tree - The parameter 'x' was not bound in the specified LINQ to Entities query expression

I am trying to build an expression tree dynamically to fetch data from a database.
The following codes are used for this.
Expression<Func<Client, bool>> expression = x => true;
foreach (var item in searchParams)
{
var operatorType = ExpressionType.Equal;
string propertyName = null;
object value = null;
string keyValue = item.Value;
if (item.Key == Constants.SearchParameterNames.Id)
{
int val = 0;
if (int.TryParse(keyValue, out val))
value = val;
propertyName = "ClientID";
}
else if (item.Key == Constants.SearchParameterNames.Lastupdate)
{
DateTime dateTime;
if (DateTime.TryParse(keyValue, out dateTime))
value = dateTime;
propertyName = "LastChange";
}
if (!string.IsNullOrWhiteSpace(propertyName) && value != null)
{
var exp = GetBinaryOperation<Client>(propertyName, operatorType, value);
var exp1 = Expression.And(expression.Body, exp);
expression = Expression.Lambda<Func<Client, bool>>(exp1, expression.Parameters);
}
}
var client = _clientRepository.FindBy(expression).ToList();
when _clientRepository.FindBy(expression).ToList() is executed I am getting an exception of
The parameter 'x' was not bound in the specified LINQ to Entities
query expression.
The method used to create expression:
public BinaryExpression GetBinaryOperation<T>(string propertyName, ExpressionType type, object value)
{
var parameterExpression = Expression.Parameter(typeof(T), "x");
var memberExpression = Expression.Property(parameterExpression, propertyName);
var propertyType = GetMemberType(memberExpression);
var rhs = Expression.Constant(value);
var binaryExpression = Expression.MakeBinary(type, memberExpression, rhs);
return binaryExpression;
}
When building such an expression you have to preserve the top-level parameter expression instance. When you create a new parameter expression in the GetBinaryOperation function, that will be a different instance (hence the not bound term), regardless of the fact that its name is the same "x".
Instead of creating a new parameter instance, you should pass the original LambdaExpression's "x" parameter to the GetBinaryOperation function using for example expression.Parameters[0].
All in all, you have to use the same parameter expression instance throughout the entire expression tree in this case.

Dynamic expression tree to filter on nested collection properties

I'm using Entity Framework and building queries using navigation properties dynamically.
For most of my use cases, the following works fine:
private static MethodCallExpression GetNavigationPropertyExpression<T>(string propertyName, int test,
ParameterExpression parameter, string subParameter)
{
var navigationPropertyCollection = Expression.Property(parameter, propertyName);
var childType = navigationPropertyCollection.Type.GetGenericArguments()[0];
var anyMethod = typeof(Enumerable).GetMethods().Single(m => m.Name == "Any" && m.GetParameters().Length == 2).MakeGenericMethod(childType);
var aclAttribute = GetAclAttribute(typeof(T), propertyName);
var childProperty = aclAttribute.ChildProperty;
var propertyCollectionGenericArg = childType;
var serviceLocationsParam = Expression.Parameter(propertyCollectionGenericArg, subParameter);
var left = Expression.Property(serviceLocationsParam, childProperty);
var right = Expression.Constant(test, typeof(int));
var isEqual = Expression.Equal(left, right);
var subLambda = Expression.Lambda(isEqual, serviceLocationsParam);
var resultExpression = Expression.Call(anyMethod, navigationPropertyCollection, subLambda);
return resultExpression;
}
I use a custom AclAttribute class assigned to properties via metadata type and partial classes. For navigation properties, a ChildProperty is provided so that the expression builder knows to look deeper for the desired property.
For example: the table Services references another table called ServiceLocations. I need the LocationId value(s) from the ServiceLocations reference. This part works fine.
My problem is when there is more than 1 nested property to go through. Another example: the table ServiceCategories references Services which, again, references ServiceLocations. Like before, I need the LocationId value(s) from ServiceLocations.
I've done this manually by using two "Any" methods, combining, and returning the resulting expression, but there will be times where navigation properties won't be collections, or a child of a navigation property may be a collection. For those cases, I need some kind of recursive option. I've been trying for a while now, and coming up short.
Since I mentioned it, here's the test method I put together for my second example. This works, but I'm violating DRY. (Note: I didn't name the tables):
private static MethodCallExpression GetNestedNavigationPropertyExpression(int test, ParameterExpression rootParameter)
{
var servicesProperty = Expression.Property(rootParameter, "tblServices");
var servicesParameter = Expression.Parameter(servicesProperty.Type.GetGenericArguments()[0], "ss");
var serviceLocationsProperty = Expression.Property(servicesParameter, "tblServiceLocations");
var serviceLocationsParameter = Expression.Parameter(serviceLocationsProperty.Type.GetGenericArguments()[0], "s");
var servicesAnyMethod = typeof(Enumerable).GetMethods().Single(m => m.Name == "Any" && m.GetParameters().Length == 2).MakeGenericMethod(servicesProperty.Type.GetGenericArguments()[0]);
var serviceLocationsAnyMethod = typeof(Enumerable).GetMethods().Single(m => m.Name == "Any" && m.GetParameters().Length == 2).MakeGenericMethod(serviceLocationsProperty.Type.GetGenericArguments()[0]);
var aclAttribute = GetAclAttribute(typeof(tblService), "tblServiceLocations");
var left = Expression.Property(serviceLocationsParameter, aclAttribute.ChildProperty);
var right = Expression.Constant(test, typeof(int));
var isEqual = Expression.Equal(left, right);
var subLambda = Expression.Lambda(isEqual, serviceLocationsParameter);
var endExpression = Expression.Call(serviceLocationsAnyMethod, serviceLocationsProperty, subLambda);
var intermediaryLamba = Expression.Lambda(endExpression, servicesParameter);
var resultExpression = Expression.Call(servicesAnyMethod, servicesProperty, intermediaryLamba);
return resultExpression;
}
With this it should be possible to build your queries:
public static Expression GetNavigationPropertyExpression(Expression parameter, int test, params string[] properties)
{
Expression resultExpression = null;
Expression childParameter, navigationPropertyPredicate;
Type childType = null;
if (properties.Count() > 1)
{
//build path
parameter = Expression.Property(parameter, properties[0]);
var isCollection = typeof(IEnumerable).IsAssignableFrom(parameter.Type);
//if it´s a collection we later need to use the predicate in the methodexpressioncall
if (isCollection)
{
childType = parameter.Type.GetGenericArguments()[0];
childParameter = Expression.Parameter(childType, childType.Name);
}
else
{
childParameter = parameter;
}
//skip current property and get navigation property expression recursivly
var innerProperties = properties.Skip(1).ToArray();
navigationPropertyPredicate = GetNavigationPropertyExpression(childParameter, test, innerProperties);
if (isCollection)
{
//build methodexpressioncall
var anyMethod = typeof(Enumerable).GetMethods().Single(m => m.Name == "Any" && m.GetParameters().Length == 2);
anyMethod = anyMethod.MakeGenericMethod(childType);
navigationPropertyPredicate = Expression.Call(anyMethod, parameter, navigationPropertyPredicate);
resultExpression = MakeLambda(parameter, navigationPropertyPredicate);
}
else
{
resultExpression = navigationPropertyPredicate;
}
}
else
{
//Formerly from ACLAttribute
var childProperty = parameter.Type.GetProperty(properties[0]);
var left = Expression.Property(parameter, childProperty);
var right = Expression.Constant(test, typeof(int));
navigationPropertyPredicate = Expression.Equal(left, right);
resultExpression = MakeLambda(parameter, navigationPropertyPredicate);
}
return resultExpression;
}
private static Expression MakeLambda(Expression parameter, Expression predicate)
{
var resultParameterVisitor = new ParameterVisitor();
resultParameterVisitor.Visit(parameter);
var resultParameter = resultParameterVisitor.Parameter;
return Expression.Lambda(predicate, (ParameterExpression)resultParameter);
}
private class ParameterVisitor : ExpressionVisitor
{
public Expression Parameter
{
get;
private set;
}
protected override Expression VisitParameter(ParameterExpression node)
{
Parameter = node;
return node;
}
}
Call it like:
var parameter = Expression.Parameter(typeof(A), "A");
var expression = ExpressionBuilder.GetNavigationPropertyExpression(parameter, 8,"CollectionOfB", "CollectionOfC", "ID");

Categories