I'm using LINQ with Expression trees and a case Statement in my Select. I'm doing this because the Where Condition is build dynamically and in my Result, I need to know, which part of the where was true.
This works fine:
ParameterExpression peTbl = Expression.Parameter(typeof(MyTbl), "mytbl");
Expression left = Expression.Property(peTbl, "Col1");
Expression right = Expression.Constant((ulong)3344, typeof(ulong));
Expression e1 = Expression.Equal(left, right);
left = Expression.Property(peTbl, "Col2");
right = Expression.Constant((ulong)27, typeof(ulong));
Expression e2 = Expression.Equal(left, right);
Expression predicateBody = Expression.Or(e1, e2);
Expression<Func<MyTbl, bool>> whereCondition = Expression.Lambda<Func<MyTbl, bool>>(predicateBody, new ParameterExpression[] { peTbl });
var query = myTbl.Where(whereCondition)
.Select(s => new { mytbl = s, mycase = (s.Col1 == 3344 ? 1 : 0) });
But now, I want to use the Expression e1 in my case Statement.
Something like this:
var query = myTbl.Where(whereCondition)
.Select(s => new { mytbl = s, mycase = (e1 == true ? 1 : 0) });
Any idea how to do this?
If you query against a database, you can commit the query first and then apply the compiled e1:
var e1Compiled = Expression.Lambda<Func<MyTbl,bool>>(e1, peTbl).Compile();
var query = myTbl
.Where(whereCondition).ToList()
.Select(s => new { mytbl = s, mycase = (e1Compiled(s) ? 1 : 0) });
if there is no database, just use the compiled e1:
var query = myTbl
.Where(whereCondition)
.Select(s => new { mytbl = s, mycase = (e1Compiled(s) ? 1 : 0) });
If you want to use a lambda expression inside another one, then here is a link ti an answer I gave to another question, where you can do it. Pass expression parameter as argument to another expression
Related
I am using the dictionary inside the IQueryable lambda linq throws the
Unable to create a constant value of type 'System.Collections.Generic.KeyValuePair`2
Code :
Dictionary<int, int> keyValues = new Dictionary<int, int>();
IQueryable<Account> = context.Account
.Where(W => keyValues
.Where(W1 => W1.Key == S.AccountID)
.Where(W1 => W1.Value == S.Balance)
.Count() > 0);
Details:
I have the data inside the dictionary like this
AccountID Balance
1 1000
2 2000
3 3000
I want the user which have the (ID = 1 AND Balance = 1000) OR (ID = 2 AND BALANCE = 2000) OR (ID = 3 AND BALANCE = 3000)
So how can I write the lambda for it ?
Edited
Thanks #caesay, Your answer help me lots.
I want one more favor from you.
From you answer I create the expression which look like below:
private static Expression<Func<Accounting, bool>> GenerateExpression(Dictionary<int, int> lstAccountsBalance)
{
try
{
var objAccounting = Expression.Parameter(typeof(Accounting));
Expression expr = null;
const bool NOT_ALLOWED = false;
if (lstAccountsBalance != null && lstAccountsBalance.Count > 0)
{
var clauses = new List<Expression>();
foreach (var kvp in lstAccountsBalance)
{
clauses.Add(Expression.AndAlso(
Expression.Equal(Expression.Constant(kvp.Key), Expression.Property(objAccounting, nameof(Accounting.ID))),
Expression.Equal(Expression.Constant(kvp.Value), Expression.Property(objAccounting, nameof(Accounting.Balance)))
));
}
expr = clauses.First();
foreach (var e in clauses.Skip(1))
{
expr = Expression.OrElse(e, expr);
}
var notAllowedExpr = Expression.AndAlso(
Expression.Equal(Expression.Constant(NOT_ALLOWED), Expression.Property(objAccounting, nameof(Accounting.ALLOWED))),
Expression.Equal(Expression.Constant(true), Expression.Constant(true))
);
expr = Expression.And(notAllowedExpr, expr);
}
var allowedExpr = Expression.AndAlso(
Expression.Equal(Expression.Constant(!NOT_ALLOWED), Expression.Property(objAccounting, nameof(Accounting.ALLOWED))),
Expression.Equal(Expression.Property(objAccounting, nameof(Accounting.ID)), Expression.Property(objAccounting, nameof(Accounting.ID)))
);
if (expr != null)
{
expr = Expression.OrElse(allowedExpr, expr);
}
else
{
expr = allowedExpr;
}
return Expression.Lambda<Func<Accounting, bool>>(expr, objAccounting);
}
catch (Exception ex)
{
throw objEx;
}
}
After that I compiled the expression like this:
Expression<Func<Accounting, bool>> ExpressionFunctions = GenerateExpression(lstAccountsBalance);
var compiledExpression = ExpressionFunctions.Compile();
And I used like this:
.Select(S => new
{
Accounting = S.Accounts
.Join(context.AccountInfo,
objAccounts => objAccounts.ID,
objAccountInfo => objAccountInfo.ID,
(objAccounts, objAccountInfo) => new Accounting
{
ID = objAccounts.ID,
Balance = objAccountInfo.Balance,
})
.Where(W => W.ID == user.ID)
.AsQueryable()
.Where(W => compiledExpression(W))
.Select(S1 => new Accounting()
{
ID = S1.ID,
Balance = S1.Balance
})
.ToList(),
}
And it throws the exception with the message:
System.NotSupportedException: The LINQ expression node type 'Invoke'
is not supported in LINQ to Entities.
Without Compile
Without the compile it works like charm. It gives the output want I want.
Expression<Func<Accounting, bool>> ExpressionFunctions = GenerateExpression(lstAccountsBalance);
Use:
.Select(S => new
{
Accounting = S.Accounts
.Join(context.AccountInfo,
objAccounts => objAccounts.ID,
objAccountInfo => objAccountInfo.ID,
(objAccounts, objAccountInfo) => new Accounting
{
ID = objAccounts.ID,
Balance = objAccountInfo.Balance,
})
.Where(W => W.ID == user.ID)
.AsQueryable()
.Where(ExpressionFunctions)
.Select(S1 => new Accounting()
{
ID = S1.ID,
Balance = S1.Balance
})
Thank you..
Everything inside of a EF linq query needs to be compiled to an Expression tree, and then to SQL, but the dictionary is an IEnumerable so there is no way that EF could know how to compile that.
You can either build the expression tree yourself, or use the System.Linq.Dynamic nuget package to build the sql yourself.
The example using System.Linq.Dynamic, first install the nuget package and add the using to the top of your file, then there will be a Where overload that takes a string as a parameter:
context.Account.Where(
String.Join(" OR ", keyValues.Select(kvp => $"(ID = {kvp.Key} AND Balance = {kvp.Value})")));
Essentially, everything inside of the Where clause will be executed directly as SQL, so be careful to use the Where(string, params object[]) overload to paramaterize your query if accepting user input.
The expression tree (untested) approach might look like the following:
var account = Expression.Parameter(typeof(Account));
var clauses = new List<Expression>();
foreach(var kvp in keyValues)
{
clauses.Add(Expression.AndAlso(
Expression.Equal(Expression.Constant(kvp.Key), Expression.Property(account, nameof(Account.AccountID))),
Expression.Equal(Expression.Constant(kvp.Value), Expression.Property(account, nameof(Account.Balance)))
));
}
var expr = clauses.First();
foreach (var e in clauses.Skip(1))
expr = Expression.OrElse(e, expr);
context.Account.Where(Expression.Lambda<Func<Account, bool>>(expr, account));
Essentially inside the foreach loop we're creating all of the (ID = ... AND Balance = ...) and then at the end we join them all with an OR.
I am new to Expressions in c#.
Expression code
var parameter = Expression.Parameter(typeof(int), "param");
var constant = Expression.Constant(5, typeof(int));
var equal = Expression.Equal(parameter, constant);
var lambdaExpression = Expression.Lambda<Func<int, bool>>(equal, parameter);
var compiledExpression = lambdaExpression.Compile();
Query contains a string value and I want to apply expresion only if the value is convertible to int
int test;
query = query.Where(i => int.TryParse(i.Key, out test) && compiledExpression(test));
This returns an error saying int.TryParse is not supported.
Any way to solve this?
You can't use out parameters there.
1) var v = q.Where(x => x.All(c => c >= '0' && c <= '9'));
2) use regex instead of All
3) write a simple method that calls Int32.Parse and just returns a bool to hide the out param
for #3:
static bool SafeIntParse(string s)
{
int n;
return Int32.TryParse(s, out n);
}
var v = q.Where(x => SafeIntParse(x));
The SafeIntParse() method is, of course, a separate static method.
EDIT:
for the regex method:
Regex regex = new Regex("^\\d+$", RegexOptions.Compiled);
var v = q.Where(x => regex.Match(x).Success);
Of course, make the regex object a static object of the class.
Combining Tables without JOIN keyword
var res2 = from u in dtEmp.AsEnumerable()
from v in dtDept.AsEnumerable()
where u.Field<int>("DepartmentID") == v.Field<int>("DepartmentID") &&
u.Field<double>("Salary") > 10000
select new
{
Name = u.Field<string>("Name"),
Department = v.Field<string>("DepartmentName")
};
How to do the same using Lambda Expression without using Join Keyword?
Do you mean you want to switch from SQL syntax to Method Chain syntax? i.e:
var res2 = dtEmp.AsEnumerable()
.SelectMany(u => dtDept.AsEnumerable(), (u, v) => new {u, v})
.Where(#t => u.Field<int>("DepartmentID") == v.Field<int>("DepartmentID") &&
u.Field<double>("Salary") > 10000).Select(#t => new
{
Name = u.Field<string>("Name"),
Department = v.Field<string>("DepartmentName")
});
i wrote function
private Func<CategorizedPosts, bool> CompileExpression(IEnumerable<Category> categories)
{
Expression predicateBody;
ParameterExpression pe = Expression.Parameter(typeof(CategorizedPosts), "post");
Expression left = Expression.Property(pe, typeof(CategorizedPosts).GetProperty("CATEGORY_ID"));
Expression right = Expression.Constant(categories.ElementAt(0).ID);
Expression equal = Expression.Equal(left, right);
predicateBody = equal;
for (int i = 1, j = categories.Count() - 1; i < categories.Count(); ++i )
{
var category = categories.ElementAt(i);
//y => y.CATEGORY_ID == 1 || y.CATEGORY_ID == 2)
left = Expression.Property(pe, typeof(CategorizedPosts).GetProperty("CATEGORY_ID"));
right = Expression.Constant(category.ID);
equal = Expression.Equal(left, right);
predicateBody = Expression.OrElse(predicateBody, equal);
}
var lll = Expression.Lambda<Func<CategorizedPosts, bool>>(predicateBody, pe);
var compiled = lll.Compile();
return compiled;
}
it compiles OK, but when I try to run this query
var ctx = db.Posts.Where(x => true);
if(predicate != null)
{
ctx = ctx.Where(x => x.CategorizedPosts.Where(**predicate**).Count() > 0);
}
IList<Post> posts = ctx.OrderByDescending(x => x.CREATION_DATE).Skip((page - 1) * perPage).Take(perPage).Select(x => new Post
{
POST_ID = x.ID,
TYPE = new Type { ID = x.TYPE_ID, NAME = x.Types.NAME },
AUTHOR = new Author()
{
ID = x.AUTHOR_ID,
NAME = x.Authors.NAME,
},
CATEGORIES = x.CategorizedPosts.Select(y => new Category() { ID = y.CATEGORY_ID, NAME = y.Categories.NAME }),
CREATION_DATE = x.CREATION_DATE,
}).ToList();
EF throws exception about internal error 1025 for Entity Data Provider. How can I perform this query with dynamic where?
You could use the Contains of a collection of Ids (int) and apply it on a where, for sample:
int[] categorieIds = categories.Select(x => x.Id).ToArray();
ctx = ctx.Where(x => x.CategorizedPosts.Any(c => categorieIds .Contains(c.Id));
Some Tips
Remember the Entity Framework works with Expression<Func<T, bool>> in the Where method, not only Func<T, bool>.
You also could try to apply PredicateBuilder class which provides some extensions methods like Or, And, Not, so, you could try this:
var predicate = PredicateBuilder.False<Product>();
foreach (string keyword in keywords)
{
string temp = keyword;
predicate = predicate.Or (p => p.Description.Contains (temp));
}
return dataContext.Products.Where(predicate).ToList();
I'm very low experienced with Expressions in .NET, that's why I rather ask you guys.
How should I - see comment below:
using P = Myclass;
..
System.Linq.Expressions.Expression<Func<P, bool>> myExpression = null;
..
myExpression1 = x => foo1 == true && foo2 == false;
myExpression2 = x => ... ;
..
BinaryExpression resultExpression = System.Linq.Expressions.Expression.OrElse(myExpression1, myExpression2);
..
IQueryable<P> l = l.Where(?resultExpression?); // how to transform BinaryExpression to the suitable type?
Thank you
You can't "OR" lambdas together that way. You really want to "OR" the lambda bodies together. Here's a method to do that:
public static Expression<Func<T, bool>> OrTheseFiltersTogether<T>(
this IEnumerable<Expression<Func<T, bool>>> filters)
{
Expression<Func<T, bool>> firstFilter = filters.FirstOrDefault();
if (firstFilter == null)
{
Expression<Func<T, bool>> alwaysTrue = x => true;
return alwaysTrue;
}
var body = firstFilter.Body;
var param = firstFilter.Parameters.ToArray();
foreach (var nextFilter in filters.Skip(1))
{
var nextBody = Expression.Invoke(nextFilter, param);
body = Expression.OrElse(body, nextBody);
}
Expression<Func<T, bool>> result = Expression.Lambda<Func<T, bool>>(body, param);
return result;
}
Then later:
Expression<Func<P, bool>> myFilter1 = x => foo1 == true && foo2 == false;
Expression<Func<P, bool>> myFilter2 = x => ... ;
..
List<Expression<Func<P, bool>>> filters = new List<Expression<Func<P, bool>>>();
filters.Add(myfilter1);
filters.Add(myfilter2);
..
Expression<Func<P, bool>> resultFilter = filters.OrTheseFiltersTogether();
IQueryable<P> query = query.Where(resultFilter);
you might want to take a wee look at the Predicatebuilder:
http://www.albahari.com/nutshell/predicatebuilder.aspx
the Predicatebuilder allows you to run up some very powerful expressions (AND/OR/NOT etc, etc) in a very clean and easy to understand way. For simple expressions, I do of course just roll them from scratch and apply but for the complex stuff...
I'm quite a fan of it :)
a few links on SO itself that may be helpful:
LINQ to SQL PredicateBuilder
Generated SQL with PredicateBuilder, LINQPad and operator ANY
.Where method takes a lambda expression as a parameter, so you need to build your BinaryExpression to a complete LambdaExpression.
var resultExpression = Expression.OrElse(myExp1, myExp2);
// now the exp is like: p > 100 || p < 10
ParameterExpression parameterExp = Expression.Parameter(typeof(P),"p");
// construct a parameter with its type P, looks like: p =>
LambdaExpression lambdaExp = Expression.Lambda(resultExpression, parameterExp);
// merge the two expressions: p => p > 100 || p < 10
myList.Where(lambdaExp.Compile());
Its a combination of two Func<P, bool> on expression level.
A less fancy way to do the same should be:
Func<P, bool> myExpression1 = x => foo1 == true && foo2 == false;
Func<P, bool> myExpression2 = x => ... ;
IQueryable<P> l = l.Where((p) => myExpression1(p) || myExpression2(p));