I am looking to take the params string[] args and convert it to a lambda expression for Entity Framework.
Something like this...
public main(params string[] args)
{
DataContext context = new DataContext();
foreach(string arg in args)
{
//build Query
}
context.Things.Where(/*Query*/);
}
You can use dynamic linq to create string based expressions.
https://weblogs.asp.net/scottgu/dynamic-linq-part-1-using-the-linq-dynamic-query-library
The downside is that these expression are evaluated at runtime, so you won't be able to catch errors at compile time.
Or you can use Expression trees to construct expressions .
https://msdn.microsoft.com/en-us/library/bb882637(v=vs.110).aspx
This will be a bit more code, but you will get the advantage of compile time type checks.
An excellent article is here https://www.codeproject.com/Articles/1079028/Build-Lambda-Expressions-Dynamically
Here is my solution:
public static Expression<Func<TClass, bool>> ConvertParamArgsToExpression<TClass>(string[] args)
{
Expression finalExpression = Expression.Constant(true);
var parameter = Expression.Parameter(typeof(TClass), "x");
foreach (string arg in args) {
string[] values = arg.Split('=');
PropertyInfo prop = typeof(TClass).GetProperty(values[0]);
if(prop != null)
{
Expression expression = null;
var member = Expression.Property(parameter, prop.Name);
var constant = Expression.Constant(values[1]);
expression = Expression.Equal(member, constant);
finalExpression = Expression.AndAlso(finalExpression, expression);
}
}
return (Expression.Lambda<Func<TClass, bool>>(finalExpression, parameter));
}
Usage:
Expression<Func<AdminPageObject, bool>> expression = LambdaConverter.ConvertParamArgsToExpression<AdminPageObject>(args);
if(expression != null)
{
items = items.Where(expression);
}
Related
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);
}
}
}
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"
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.
I have a basic rule engine that I've built in a very similar way to the route suggested here:
How to implement a rule engine?
Ive extended it based on further requirements, and now I need to evaluate complex classes eg
EvaluateRule("Transaction.IsOpen", "Equals", "true")
The code in its most basic form is:
var param = inputMessageType;
left = Expression.Property(param, memberName);
tProp = typeof(T).GetProperty(r.MemberName).PropertyType;
right = Expression.Constant(Convert.ChangeType(r.TargetValue, tProp));
return Expression.MakeBinary(tBinary, left, right);
In order to evaluate complex classes I used a method similar to here:
https://stackoverflow.com/questions/16544672/dynamically-evaluating-a-property-string-with-expressions
The problem that Im having is that when I try to evaluate the rule with a property of a class (Transaction.IsOpen), I get it with the type of the root type on the right hand side of the expression but the type of the complex object on the left hand side of the expression.
This results in the error:
System.InvalidOperationException: The binary operator Equal is not defined for the types 'System.Func`2[Transaction,System.Boolean]' and 'System.Boolean'.
How do I overcome this problem? I am no expert using Expression Trees, and many of the concepts are proving difficult to grasp when the example strays from the standard documentation.
Edit: Here is the code(Ive omitted some stuff that is environment specific so as keep focus with the problem)
public Actions EvaluateRulesFromMessage(ClientEventQueueMessage message)
{
var ruleGroups = _ruleRepository.GetRuleList();
var actions = new Actions();
foreach (var ruleGroup in ruleGroups)
{
if (message.MessageType == "UI_UPDATE")
{
// clean up json object
JObject dsPayload = (JObject.Parse(message.Payload));
var msgParams = JsonConvert.DeserializeObject<UiTransactionUpdate>(message.Payload);
msgParams.RulesCompleted = msgParams.RulesCompleted ?? new List<int>();
var conditionsMet = false;
// process the rules filtering out the rules that have already been evaluated
var filteredRules = ruleGroup.Rules.Where(item =>
!msgParams.RulesCompleted.Any(r => r.Equals(item.Id)));
foreach (var rule in filteredRules)
{
Func<UiTransactionUpdate, bool> compiledRule = CompileRule<UiTransactionUpdate>(rule, msgParams);
if (compiledRule(msgParams))
{
conditionsMet = true;
}
else
{
conditionsMet = false;
break;
}
}
if (conditionsMet)
{
actions = AddAction(message, ruleGroup);
break;
}
}
}
return actions;
}
public Func<UiTransactionUpdate, bool> CompileRule<T>(Rule r, UiTransactionUpdate msg)
{
var expression = Expression.Parameter(typeof(UiTransactionUpdate));
Expression expr = BuildExpr<UiTransactionUpdate>(r, expression, msg);
// build a lambda function UiTransactionUpdate->bool and compile it
return Expression.Lambda<Func<UiTransactionUpdate, bool>>(expr, expression).Compile();
}
static Expression Eval(object root, string propertyString, out Type tProp)
{
Type type = null;
var propertyNames = propertyString.Split('.');
ParameterExpression param = Expression.Parameter(root.GetType());
Expression property = param;
string propName = "";
foreach (var prop in propertyNames)
{
property = MemberExpression.PropertyOrField(property, prop);
type = property.Type;
propName = prop;
}
tProp = Type.GetType(type.UnderlyingSystemType.AssemblyQualifiedName);
var param2 = MemberExpression.Parameter(tProp);
var e = Expression.Lambda(property, param);
return e;
}
static Expression BuildExpr<T>(Rule r, ParameterExpression param, UiTransactionUpdate msg)
{
Expression left;
Type tProp;
string memberName = r.MemberName;
if (memberName.Contains("."))
{
left = Eval(msg, memberName, out tProp);
}
else
{
left = Expression.Property(param, memberName);
tProp = typeof(T).GetProperty(r.MemberName).PropertyType;
}
ExpressionType tBinary;
if (ExpressionType.TryParse(r.Operator, out tBinary))
{
Expression right=null;
switch (r.ValueType) ///todo: this needs to be refactored to be type independent
{
case TargetValueType.Value:
right = Expression.Constant(Convert.ChangeType(r.TargetValue, tProp));
break;
}
// use a binary operation ie true/false
return Expression.MakeBinary(tBinary, left, right);
}
else
{
var method = tProp.GetMethod(r.Operator);
var tParam = method.GetParameters()[0].ParameterType;
var right = Expression.Constant(Convert.ChangeType(r.TargetValue, tParam));
// use a method call, e.g. 'Contains' -> 'u.Tags.Contains(some_tag)'
return Expression.Call(left, method, right);
}
}
The sample code does not cover all data types used in your scenario, so it's hard to tell where it breaks exactly, but from the exception System.Func'2[Transaction,System.Boolean]' and 'System.Boolean it's obvious that on left hand you have a delegate that takes in Transaction and returns bool (Func<Transaction, bool>), and on the right hand you just have bool.
It's not possible to compare Func<Transaction, bool> to bool, but it's possible to call the function and compare its result:
Func<Transaction, bool> func = ...;
bool comparand = ...;
Transaction transaction = ...;
if (func(transaction) == comparand) { ... }
What translated to expression tree:
Expression funcExpression = ... /*LambdaExpression<Func<Transaction,bool>>*/;
Expression comparandExpression = Expression.Constant(true);
Expression transactionArg = /*e.g.*/Expression.Constant(transaction);
Expression funcResultExpression = Expression.Call(funcExpression, "Invoke", null, transactionArg);
Expression equalityTestExpression = Expression.Equal(funcResultExpression, comparandExpression);
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)));