I'm having problem with a lambda expression which is used for custom filtering of data from DataGridView.
This is the expression:
private static Expression<Func<T, bool>> ExpressionLongEquals<T>(string Key, long Value)
{
var param = Expression.Parameter(typeof(T));
// create expression for param => param.TEntityNameId == PrimaryKey
var lambda = Expression.Lambda<Func<T, bool>>(
Expression.Equal(
Expression.Property(param, Key),
Expression.Constant(Value)),
param);
return lambda;
}
The problem is that I have a case when the Value argument is of type long? which as it seems is acceptable but after completing this code I get an error that Method equal is not defined for Nullable1.System.Int64 and System.Int64. I have difficulties with understanding this method, not very sure that it's understandable for the others outside the context but however I'm gonna post my questions - first, what exactly is this, by which I mean - what I need to learn/read in order to be able with such kind of code like the one I posted and second. I'm pretty sure this method works with long values and make problems only when long? is passed as argument so is there any way to modify it to solve this problem?
The need of long?values is recent and this is what cause the problem generally I do this:
else if (property.PropertyType == typeof(long?))
{
long value = Convert.ToInt64(rule.Data);
selector = ExpressionLongEquals<T>(rule.Field, value);
}
but still I get the error about equal not defined for Nullable1.System.Int64 and System.Int64.
USE this...If I am correct you have to convert your value to a type of long?
private static Expression<Func<T, bool>> ExpressionLongEquals<T>(string Key, long Value)
{
var param = Expression.Parameter(typeof(T));
// create expression for param => param.TEntityNameId == PrimaryKey
var lambda = Expression.Lambda<Func<T, bool>>(
Expression.Equal(
Expression.Property(param, Key),
Expression.Constant(Value, typeof(long?)),
param);
return lambda;
}
Related
How can I create a property selector for entity framework like this?
public static List<T> StandardSearchAlgorithm<T>(this IQueryable<T> queryable, Func<T, string> property, string query)
{
return queryable.Where(e => property(e).ToLower().IndexOf(query) > -1).ToList();
}
I want the calling code to be able to be clean and simple like this:
var usernameResults = _db.Users.StandardSearchAlgorithm(u => u.Username, query);
I get a "The LINQ expression node type 'Invoke' is not supported in LINQ to Entities." error. I cannot work out how to get the expression built.
UPDATE:
Based on the answer by MBoros here is the code I ended up with. It works great.
The key to expression trees is to understand expression trees are all about breaking up what you normally write in code (like "e => e.Username.IndexOf(query)") into a series of objects: "e" gets its own object, "Username" its own object, "IndexOf()" its own object, the "query" constant its own object, and so on. The second key is to know that you can use a series of static methods on the Expression class to create various kinds of these objects, as shown below.
PropertyInfo pinfo = (PropertyInfo)((MemberExpression)property.Body).Member;
ParameterExpression parameter = Expression.Parameter(typeof(T), "e");
MemberExpression accessor = Expression.Property(parameter, pinfo);
ConstantExpression queryString = Expression.Constant(query, typeof(string));
ConstantExpression minusOne = Expression.Constant(-1, typeof(int));
MethodInfo indexOfInfo = typeof(string).GetMethod("IndexOf", new[] { typeof(string) }); // easiest way to do this
Expression indexOf = Expression.Call(accessor, indexOfInfo, queryString);
Expression expression = Expression.GreaterThan(indexOf, minusOne);
Expression<Func<T, bool>> predicate = Expression.Lambda<Func<T, bool>>(expression, parameter);
//return predicate.Body.ToString(); // returns "e => e.Username.IndexOf(query) > -1" which is exactly what we want.
var results = queryable.Where(predicate).ToList();
return results;
Now I have a real problem, but I will ask it in a separate question. My real query looks like this:
public static List<T> StandardSearchAlgorithm<T>(this IQueryable<T> queryable, Func<T, string> property, string query)
{
return queryable.Where(e => property(e).IndexOf(query) > -1).Select(e=> new { Priority = property(e).IndexOf(query), Entity = e } ).ToList();
}
So I need to build an expression that returns an Anonymous Type!! Or even if I create a class to help, I need to write an expression that returns a new object. But I will include this in a separate question.
You cannot invoke CLR delegates so simply in sql. But you can pass in the property selector as an Expression tree., so your signature would be:
public static List<T> StandardSearchAlgorithm<T>(this IQueryable<T> queryable, Expression<Func<T, string>> property, string query)
Calling would look the same. But now that you have an expression in your hand, you can have a look at this answer:
Pass expression parameter as argument to another expression
It gives you the tools to simply put an expression tree inside another one. In your case it would look like:
Expression<Func<T, bool>> predicate = e => property.AsQuote()(e).Contains(query);
predicate = predicate.ResolveQuotes();
return queryable.Where(predicate).ToList();
Once you are there, you still have the .ToLower().Contains() calls (use .Contains instead of the .IndexOf()> 1). This is actually tricky. Normally the db uses its default collation, so if it set to CI (case insensitive), then it will do the compare that way. If you don't have any constraints, and can adjust the db collation, I would go for that. In this case you can omit the .ToLower() call.
Otherwise check out this anser: https://stackoverflow.com/a/2433217/280562
I've been doing research on how to dynamically make queries for linq. I'm creating a UI for a web application which allows them to select properties from a DB and set where clauses to build reports dynamically. All of the questions I've seen on stack or other sites reference using Dynamic Linq and LinqKit to Solve the problem, example Here . However I can't find an solution to express the syntax.
// This attempts to grab out a title whose from the Database whose
// Copyright Date equals 2006
propName = "CopyrightDate";
Model.Titles.Where(t => t.GetType().GetProperty(propName).GetValue(t, null) == "2006");
I want to do something like this but in Linq to Entities. Linq to entities doesn't support reflection like that, I do not want to pull out all of the data and run Linq to Object the DB is too Large. Any Suggestions on how to write this in Dynamic Linq. Bonus points if you can cast the type to the property type so It can be evaultuated with standard operators (== , > , < , etc..).
What the LINQ-to-entities Where extension method wants, is an Expression<Func<T, bool>>. You can create such an expression like this:
// Create lambda expression: t => t.CopyrightDate == "2006"
ParameterExpression parameter = Expression.Parameter(typeof(T), "t");
var lambda = Expression.Lambda<Func<T, bool>>(
Expression.Equal(
Expression.Property(parameter, "CopyrightDate"),
Expression.Constant("2006")
),
parameter
);
where T is the type of your class containing the CopyrightDate property.
var result = context.Titles
.Where(lambda)
.ToList();
A somewhat more general solution is:
Expression<Func<T, bool>> Comparison<T>(ExpressionType comparisonType,
string propertyName, object compareTo)
{
ParameterExpression parameter = Expression.Parameter(typeof(T), "t");
MemberExpression property = Expression.Property(parameter, propertyName);
ConstantExpression constant = Expression.Constant(compareTo);
Expression body = Expression.MakeBinary(comparisonType, property, constant);
return Expression.Lambda<Func<T, bool>>(body, parameter);
}
Here is how you can rewrite your query using Dynamic Linq:
var propName = "CopyrightDate";
var propValue = "2006";
Model.Titles.Where(string.Format("{0}=#0", propName), propValue);
string.Format("{0}=#0", propName) produces the query string for the Where clause, which would be "CopyrightDate=#0" in this case. #0 specifies the parameter for the query, which becomes propValue.
Here is my code as following:
protected Expression<Func<T, bool>> GetLambdaForField(string SearchTxtBox)
{
// Build a Lamda expression to get the Node headers for this sub master.
ConstantExpression constForeignKeyID = Expression.Constant(SearchTxtBox);
ParameterExpression paramEntity =
Expression.Parameter(typeof(T), "e");
MemberExpression mex =
LambdaExpression.PropertyOrField(paramEntity, DefaultSearchFieldName);
BinaryExpression filter = Expression.Equal(mex, constForeignKeyID);
Expression<Func<T, bool>> exprLambda =
Expression.Lambda<Func<T, bool>>(filter,
new ParameterExpression[] { paramEntity });
return exprLambda;
}
I have used Expression.Equal(...); but this is not what I actually wanted to achieve.
I wanted something like the Expression.Contains method (so that it can find the record with partial information without having me to enter the complete value).
You want to use Expression.Call method:
var filter = Expression.Call(mex,
typeof(string).GetMethod("Contains"),
constForeignKeyID);
This is equal to the following expression:
x => x.Prop.Contains(searchString)
Hope this helps.
I'm dynamically building linq queries for nHibernate.
Due to dependencies, I wanted to cast/retrieve the typed expression at a later time, but I have been unsuccessfull so far.
This is not working (the cast is supposed to happen elsewhere):
var funcType = typeof (Func<,>).MakeGenericType(entityType, typeof (bool));
var typedExpression = (Func<T, bool>)Expression.Lambda(funcType, itemPredicate, parameter); //Fails
This is working:
var typedExpression = Expression.Lambda<Func<T, bool>>(itemPredicate, parameter);
Is it possible to get the 'encapsulated' typed expression from a LambdaExpression?
var typedExpression =
(Func<T, bool>)Expression.Lambda(funcType, itemPredicate, parameter); //Fails
This is not surprising, as you have to Compile a LambdaExpression in order to get an actual delegate that can be invoked (which is what Func<T, bool> is).
So this would work, but I 'm not sure if it is what you need:
// This is no longer an expression and cannot be used with IQueryable
var myDelegate =
(Func<T, bool>)
Expression.Lambda(funcType, itemPredicate, parameter).Compile();
If you are not looking to compile the expression but instead to move an expression tree around, then the solution is to instead cast to an Expression<Func<T, bool>>:
var typedExpression = (Expression<Func<T, bool>>)
Expression.Lambda(funcType, itemPredicate, parameter);
I am trying to dynamically create a linq expression with the contains operator.
After reading several posts related to this topic, I have come up with the following code that allows me to perform a "contains" like evaluation:
internal static Expression<Func<TEntity, bool>> StringContains<TEntity>(string propertyName, string subString)
{
MethodInfo contains = typeof(JsonLinqParser_Paser).GetMethod("Like");
var param = Expression.Parameter(typeof(TEntity));
var body = Expression.Call(contains, Expression.Property(param, propertyName), Expression.Constant(subString, typeof(string)));
var lambda = Expression.Lambda<Func<TEntity, bool>>(body, param);
return lambda;
}
public static bool Like(string a, string b)
{
return a.Contains(b);
}
And this is called like so:
var expression = Expression.Lambda<Func<TEntity, bool>>(StringContains<TEntity>("FIPSCO_STR", _myStringValue), param);
However, at runtime, I get an error as follows:
Expression of type
'System.Func`2[DAL.BestAvailableFIP,System.Boolean]' cannot be used
for return type 'System.Boolean'
where "DAL.BestAvailableFIP" is the "TEntity" type.
I'm sure this is all related to my lack of knowledge regarding lambda expressions. Can anyone tell me what I'm doing wrong?
StringContains already returns a LambdaExpression.
You shouldn't put it in another lambda expression.
If you want to create another lambda expression that contains it, you should use its Body.