Why my LINQ expression do not work in LINQ to SQL? - c#

private System.Linq.Expressions.Expression<Func<ActionLogs, bool>> GetExpression()
{
Expression<Func<ActionLogs, bool>> expr = w => w.ID != -1;
if (ActionDate != null)
{
Expression<Func<ActionLogs, bool>> byDate = w => w.DateAction == ActionDate;
var body = Expression.AndAlso(expr.Body, byDate.Body);
expr = Expression.Lambda<Func<ActionLogs, bool>>(body, expr.Parameters[0]);
}
if (ActionType != ActionTypeEnum.Empty)
{
Expression<Func<ActionLogs, bool>> byActionType = w => w.ActionTypeID == (int)ActionType;
var body = Expression.AndAlso(expr.Body, byActionType.Body);
expr = Expression.Lambda<Func<ActionLogs, bool>>(body, expr.Parameters[0]);
}
if (!String.IsNullOrWhiteSpace(AuthorLogin))
{
Expression<Func<ActionLogs, bool>> byLogin = w => w.User.LoginName == AuthorLogin;
var body = Expression.AndAlso(expr.Body, byLogin.Body);
expr = Expression.Lambda<Func<ActionLogs, bool>>(body, expr.Parameters[0]);
}
if (!String.IsNullOrWhiteSpace(AdditionalInfo))
{
Expression<Func<ActionLogs, bool>> byAdditionalInfo = w => w.DescriptionText.Contains(AdditionalInfo);
var body = Expression.AndAlso(expr.Body, byAdditionalInfo.Body);
expr = Expression.Lambda<Func<ActionLogs, bool>>(body, expr.Parameters[0]);
}
return expr;
}
This is the function that generate my expression.
and when i do this:
System.Linq.Expressions.Expression<Func<ActionLogs, bool>> expr = GetExpression();
result = blablabla.Where(expr);
It says to me that The parameter 'w' is not in scope.
So the question is, how i can generate my expression, that depends on something i need, and paste it into LINQ to SQL query?

public Expression<Func<T, bool>> Combine<T>(Expression<Func<T, Boolean>> first, Expression<Func<T, Boolean>> second)
{
var toInvoke = Expression.Invoke(second, first.Parameters.Cast<Expression>());
return (Expression.Lambda<Func<T, Boolean>>(Expression.AndAlso(first.Body, toInvoke), first.Parameters));
}
This function was very helpful. It combined two expressions and built correct tree.

Could it be that you are trying to return an Expression.Lambda<T> and then assign it to an object that is a SON of it on its hierarchy(System.Linq.Expressions.Expression<T>)
Please take a look at http://msdn.microsoft.com/en-us/library/system.linq.expressions.lambdaexpression.aspx
Hope that helps,

Related

Dynamic LINQ method for IndexOf

I'm trying to make use of dynamic LINQ and I'm stuck. This is what I'm aiming at:
In my testData I have names like: Lille, Nille and Bob. I want Lille and Nille in the result.
var p = CreateParameterExpression<Individual>();
var indexOf = IndexOfExpression<Individual>(p, "Name", "ill");
var exp = EQ(p, indexOf, 1);
var result= testData.Where(exp);
The missing piece of code looks like this:
static Expression<Func<T, bool>> EQ<T>(ParameterExpression parameterExp, Expression<Func<T, int>> func1, int value)
{
if (true)
{
return f => true;
}
else
{
return f => false;
}
}
I can't figure out what to put here: "if (true)"
Please help
I watched a video on YouTube that kicked me in the right direction. Her's my code:
static Expression<Func<T, bool>> EQ<T>(ParameterExpression parameterExp, Expression<Func<T, int>> func1, int value)
{
var exprBody = Expression.Equal(func1.Body, Expression.Constant(value));
var finalExpr = Expression.Lambda<Func<T, bool>>(exprBody, parameterExp);
return finalExpr;
}

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

How to append expressions in linq?

Net core application. I have a generic repository pattern implemented. I am trying to implement some filtering functionality. I have the below code.
var param = Expression.Parameter(typeof(SiteAssessmentRequest), "x");
Expression<Func<SiteAssessmentRequest, bool>> query;
query = x => x.CreatedBy == request.Userid || x.AssignedTo == request.Userid;
Expression body = Expression.Invoke(query, param);
if (request.Client != null && request.Client.Length != 0)
{
Expression<Func<SiteAssessmentRequest, bool>> internalQuery = x => request.Client.Contains(x.Client);
body = Expression.AndAlso(Expression.Invoke(query, param), Expression.Invoke(internalQuery, param));
}
if (request.CountryId != null && request.CountryId.Length != 0)
{
Expression<Func<SiteAssessmentRequest, bool>> internalQuery = x => request.CountryId.Contains(x.CountryId);
body = Expression.AndAlso(Expression.Invoke(query, param), Expression.Invoke(internalQuery, param));
}
if (request.SiteName != null && request.SiteName.Length != 0)
{
Expression<Func<SiteAssessmentRequest, bool>> internalQuery = x => request.SiteName.Contains(x.SiteName);
body = Expression.AndAlso(Expression.Invoke(query, param), Expression.Invoke(internalQuery, param));
}
if (request.Status != null && request.Status.Length != 0)
{
Expression<Func<SiteAssessmentRequest, bool>> internalQuery = x => request.Status.Contains(x.Status);
body = Expression.AndAlso(Expression.Invoke(query, param), Expression.Invoke(internalQuery, param));
}
var lambda = Expression.Lambda<Func<SiteAssessmentRequest, bool>>(body, param);
var siteAssessmentRequest = await _siteAssessmentRequestRepository.GetAsync(lambda, null, x => x.Country).ConfigureAwait(false);
In the above code when I pass more than one parameter, for example, request. Status and request.SiteName I want to filter based on status and Sitename. When I see the query only one parameter appended in the query
{x => (Invoke(x => (Not(IsNullOrEmpty(x.CreatedBy)) AndAlso Not(IsNullOrWhiteSpace(x.CreatedBy))), x)
AndAlso Invoke(x => value(Site.V1.Implementation.GetSARByFilterAr+<>c__DisplayClass12_0)
.request.Status.Contains(x.Status), x))}
After searching so much I got below code
public static class ExpressionCombiner
{
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> exp, Expression<Func<T, bool>> newExp)
{
// get the visitor
var visitor = new ParameterUpdateVisitor(newExp.Parameters.First(), exp.Parameters.First());
// replace the parameter in the expression just created
newExp = visitor.Visit(newExp) as Expression<Func<T, bool>>;
// now you can and together the two expressions
var binExp = Expression.And(exp.Body, newExp.Body);
// and return a new lambda, that will do what you want. NOTE that the binExp has reference only to te newExp.Parameters[0] (there is only 1) parameter, and no other
return Expression.Lambda<Func<T, bool>>(binExp, newExp.Parameters);
}
class ParameterUpdateVisitor : ExpressionVisitor
{
private ParameterExpression _oldParameter;
private ParameterExpression _newParameter;
public ParameterUpdateVisitor(ParameterExpression oldParameter, ParameterExpression newParameter)
{
_oldParameter = oldParameter;
_newParameter = newParameter;
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (object.ReferenceEquals(node, _oldParameter))
return _newParameter;
return base.VisitParameter(node);
}
}
}
But I am struggling to make it to work above code.
In the above query, I see only status but not site name. So I want to include multiple expressions. can someone help me regarding this? Any help would be greatly appreciated. Thanks
Let me explain core concept of LambdaExpression. LambdaExpression has 0..N parameters and Body. Body of LambdaExpression is Expression which actually we want to reuse.
Basic mistake is trying to combine lambda expressions (changed parameter names because it is how ExpressionTree works - it compares parameters by reference, not by name):
Expression<Func<Some, bool>> lambda1 = x1 => x1.Id == 10;
Expression<Func<Some, bool>> lambda2 = x2 => x2.Value == 20;
// wrong
var resultExpression = Expression.AndAlso(lambda1, lambda2);
Schematically previous wrong sample should looks like (even it will crash)
(x1 => x1.Id == 10) && (x2 => x2.Value == 20)
What we have - not desired expression but combination of "functions".
So let reuse body of LambdaExpression
// not complete
var newBody = Expression.AndAlso(lambda1.Body, lambda2.Body);
Result is more acceptable but still needs corrections:
(x1.Id == 10) && (x2.Value == 20)
Why we need correction? Because we are trying to build the following LambdaExpression
var param = Expression.Parameter(typeof(some), "e");
var newBody = Expression.AndAlso(lambda1.Body, lambda2.Body);
// still wrong
var newPredicate = Expression.Lambda<Func<Some, bool>>(newBody, param)
Result of newPredicate should looks like
e => (x1.Id == 10) && (x2.Value == 20)
As you can see we left in body parameters from previous two lambdas, which is wrong and we have to replace them with new parameter param ("e") and better to do that before combination.
I'm using my implementation of replacer, which just returns desired body.
So, let's write correct lambda!
Expression<Func<Some, bool>> lambda1 = x1 => x1.Id == 10;
Expression<Func<Some, bool>> lambda2 = x2 => x2.Value == 20;
var param = Expression.Parameter(typeof(Some), "e");
var newBody = Expression.AndAlso(
ExpressionReplacer.GetBody(lambda1, param),
ExpressionReplacer.GetBody(lambda2, param));
// hurray!
var newPredicate = Expression.Lambda<Func<Some, bool>>(newBody, param);
var query = query.Where(newPredicate);
After theses steps you will have desired result:
e => (e.Id == 10) && (e.Value == 20)
And ExpressionReplacer implementation
class ExpressionReplacer : ExpressionVisitor
{
readonly IDictionary<Expression, Expression> _replaceMap;
public ExpressionReplacer(IDictionary<Expression, Expression> replaceMap)
{
_replaceMap = replaceMap ?? throw new ArgumentNullException(nameof(replaceMap));
}
public override Expression Visit(Expression exp)
{
if (exp != null && _replaceMap.TryGetValue(exp, out var replacement))
return replacement;
return base.Visit(exp);
}
public static Expression Replace(Expression expr, Expression toReplace, Expression toExpr)
{
return new ExpressionReplacer(new Dictionary<Expression, Expression> { { toReplace, toExpr } }).Visit(expr);
}
public static Expression Replace(Expression expr, IDictionary<Expression, Expression> replaceMap)
{
return new ExpressionReplacer(replaceMap).Visit(expr);
}
public static Expression GetBody(LambdaExpression lambda, params Expression[] toReplace)
{
if (lambda.Parameters.Count != toReplace.Length)
throw new InvalidOperationException();
return new ExpressionReplacer(Enumerable.Zip(lambda.Parameters, toReplace, (f, s) => Tuple.Create(f, s))
.ToDictionary(e => (Expression)e.Item1, e => e.Item2)).Visit(lambda.Body);
}
}
If you plan to work with ExpressionTree closer, I suggest to install this VS extension, which should simplify your life: Readable Expressions

C# Expressions - Creating an Expression from another Expression

I am trying to create a reusable method using expressions that looks something like this:
Expression<Func<Order, bool>> CreateExpression(Expression<Func<Order, int>> parameter, FilterOperator operator, int value)
So I can use it like this:
IQueryable<Order> orders = db.Orders;
var filtered = orders.Where(CreateExpression(o => o.OrderID, FilterOperator.GreaterThan, 100));
I'm not sure how to write the method though. How can I write a method that will create this Expression for me?
I need to be able to do something like this:
if(operator == FilterOperator.GreaterThan)
return m => m.OrderID > value;
else if(operator == FilterOperator.LessThan)
return m => m.OrderID < value;
But I want to use the expression that is passed in instead of using OrderID directly. How can I do this?
static Expression<Func<T, bool>> CreateExpression<T>(Expression<Func<T, int>> parameter, FilterOperator #operator, int value)
{
var argExpr = Expression.Parameter(typeof(T), "p");
var paramExpr = Expression.Invoke(parameter, argExpr);
var constExpr = Expression.Constant(value);
Expression compExpr = null;
switch(#operator)
{
case FilterOperator.GreaterThan:
compExpr = Expression.GreaterThan(paramExpr, constExpr);
break;
case FilterOperator.LessThan:
compExpr = Expression.LessThan(paramExpr, constExpr);
break;
}
return Expression.Lambda<Func<T, bool>>(compExpr, argExpr);
}
If you can't use Invoke and your parameter expression is a member expression, then you can just re-create it using your new parameter:
static Expression<Func<T, bool>> CreateExpression<T>(Expression<Func<T, int>> parameter, FilterOperator #operator, int value)
{
var memberExpr = (MemberExpression)parameter.Body;
PropertyInfo property = (PropertyInfo)memberExpr.Member;
var argExpr = Expression.Parameter(typeof(T), "p");
var propertyExpr = Expression.Property(argExpr, property);
var constExpr = Expression.Constant(value);
Expression compExpr = null;
switch(#operator)
{
case FilterOperator.GreaterThan:
compExpr = Expression.GreaterThan(propertyExpr, constExpr);
break;
case FilterOperator.LessThan:
compExpr = Expression.LessThan(propertyExpr, constExpr);
break;
}
return Expression.Lambda<Func<T, bool>>(compExpr, argExpr);
}

Dynamically Build Linq Lambda Expression

Okay, my guess is this is answered somewhere already, and I'm just not quite familiar enough with the syntax yet to understand, so bear with me.
The users of my web app need to filter a long list of items in a gridview, accessed via a linqdatasource. I am using the OnSelecting Event to further filter items. I want to filter those items based on selections the users make in DropDownLists.
For example, they select "Title" "Contains" "Fred"
This results in
e.Result = dbContext.Opps.Where(opp => opp.Title.Contains("Fred"));
Or "Description" "Does not Contain" "Alpha"
results in
e.Result = dbContext.Opps.Where(opp => !opp.Description.Contains("Alpha"));
I would like to build up that Expression (System.Linq.Expressions.Expression>) dynamically, instead of having nested switch expressions to generate it, as there are a number of fields I want to check, and I also want to use the StartsWith and EndsWith checks. If I could build the Expression as a string, like so:
string stringExpression = string.Format("opp => opp.{0}.{1}(\"{2}\")",
ddlCustomFilter.SelectedValue,
ddlFilterType.SelectedValue,
txtFilterText.Text);
And then somehow have it be converted into an Expression... is this possible? Or should I just bite the bullet and generate all the switch() statements required to create the various expressions?
You could certainly build the expression dynamically, but I would consider using Dynamic LINQ as an alternative first, though you may not be able to use Contains. In addition, you may want to consider using PredicateBuilder to build complex queries additively.
try this code...
call ToExpression Method()....
public static Expression<Func<T, bool>> ToExpression<T>(string andOrOperator, string propName, string opr, string value, Expression<Func<T, bool>> expr = null)
{
Expression<Func<T, bool>> func = null;
try
{
ParameterExpression paramExpr = Expression.Parameter(typeof(T));
var arrProp = propName.Split('.').ToList();
Expression binExpr = null;
string partName = string.Empty;
arrProp.ForEach(x =>
{
Expression tempExpr = null;
partName = partName.IsNull() ? x : partName + "." + x;
if (partName == propName)
{
var member = NestedExprProp(paramExpr, partName);
var type = member.Type.Name == "Nullable`1" ? Nullable.GetUnderlyingType(member.Type) : member.Type;
tempExpr = ApplyFilter(opr, member, Expression.Convert(ToExprConstant(type, value), member.Type));
}
else
tempExpr = ApplyFilter("!=", NestedExprProp(paramExpr, partName), Expression.Constant(null));
if (binExpr != null)
binExpr = Expression.AndAlso(binExpr, tempExpr);
else
binExpr = tempExpr;
});
Expression<Func<T, bool>> innerExpr = Expression.Lambda<Func<T, bool>>(binExpr, paramExpr);
if (expr != null)
innerExpr = (andOrOperator.IsNull() || andOrOperator == "And" || andOrOperator == "AND" || andOrOperator == "&&") ? innerExpr.And(expr) : innerExpr.Or(expr);
func = innerExpr;
}
catch { }
return func;
}
private static MemberExpression NestedExprProp(Expression expr, string propName)
{
string[] arrProp = propName.Split('.');
int arrPropCount = arrProp.Length;
return (arrPropCount > 1) ? Expression.Property(NestedExprProp(expr, arrProp.Take(arrPropCount - 1).Aggregate((a, i) => a + "." + i)), arrProp[arrPropCount - 1]) : Expression.Property(expr, propName);
}
private static Expression ToExprConstant(Type prop, string value)
{
if (value.IsNull())
return Expression.Constant(value);
object val = null;
switch (prop.FullName)
{
case "System.Guid":
val = value.ToGuid();
break;
default:
val = Convert.ChangeType(value, Type.GetType(prop.FullName));
break;
}
return Expression.Constant(val);
}
private static Expression ApplyFilter(string opr, Expression left, Expression right)
{
Expression InnerLambda = null;
switch (opr)
{
case "==":
case "=":
InnerLambda = Expression.Equal(left, right);
break;
case "<":
InnerLambda = Expression.LessThan(left, right);
break;
case ">":
InnerLambda = Expression.GreaterThan(left, right);
break;
case ">=":
InnerLambda = Expression.GreaterThanOrEqual(left, right);
break;
case "<=":
InnerLambda = Expression.LessThanOrEqual(left, right);
break;
case "!=":
InnerLambda = Expression.NotEqual(left, right);
break;
case "&&":
InnerLambda = Expression.And(left, right);
break;
case "||":
InnerLambda = Expression.Or(left, right);
break;
case "LIKE":
InnerLambda = Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), right);
break;
case "NOTLIKE":
InnerLambda = Expression.Not(Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), right));
break;
}
return InnerLambda;
}
public static Expression<Func<T, object>> PropExpr<T>(string PropName)
{
ParameterExpression paramExpr = Expression.Parameter(typeof(T));
var tempExpr = Extentions.NestedExprProp(paramExpr, PropName);
return Expression.Lambda<Func<T, object>>(Expression.Convert(Expression.Lambda(tempExpr, paramExpr).Body, typeof(object)), paramExpr);
}
public static IQueryOver<T, T> OrderBy<T>(this IQueryOver<T, T> Collection, string sidx, string sord)
{
return sord == "asc" ? Collection.OrderBy(NHibernate.Criterion.Projections.Property(sidx)).Asc : Collection.OrderBy(NHibernate.Criterion.Projections.Property(sidx)).Desc;
}
public static Expression<Func<T, TResult>> And<T, TResult>(this Expression<Func<T, TResult>> expr1, Expression<Func<T, TResult>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, TResult>>(Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
}
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>(Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
}

Categories