What's wrong with this linq expression creation (using contains) - c#

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.

Related

How to build a simple property selector expression in ef6

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

Dynamic Where Linq to Entities

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.

"Does Not Contain" dynamic lambda expression

The code below does "Contain" expression:
private static Expression<Func<T, bool>> Contains<T>(string property, string value)
{
var obj = Expression.Parameter(typeof(T), "obj");
var objProperty = Expression.PropertyOrField(obj, property);
var contains = Expression.Call(objProperty, "Contains", null, Expression.Constant(value, typeof(string)));
var lambda = Expression.Lambda<Func<T, bool>>(contains, obj);
return lambda;
}
I am not very familiar with Expressions and I don't know how to put negation into the expression function and cannot find any suitable method in "Expression" class. Is there any similar way to create "Does Not Contain" expression dynamically?
A "does not contain" expression is exactly the same as a "does contain" expression - but wrapped with a unary negation expression. So basically you want:
// Code as before
var doesNotContain = Expression.Not(contains);
return Expression.Lambda<Func<T, bool>>(doesNotContain, obj);
Contains returns you a bool. Inverting a bool is done with the Expression.Not method.

lambda expression meaning/modificating

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

Converting a Linq expression tree that relies on SqlMethods.Like() for use with the Entity Framework

I recently switched from using Linq to Sql to the Entity Framework. One of the things that I've been really struggling with is getting a general purpose IQueryable extension method that was built for Linq to Sql to work with the Entity Framework. This extension method has a dependency on the Like() method of SqlMethods, which is Linq to Sql specific. What I really like about this extension method is that it allows me to dynamically construct a Sql Like statement on any object at runtime, by simply passing in a property name (as string) and a query clause (also as string). Such an extension method is very convenient for using grids like flexigrid or jqgrid. Here is the Linq to Sql version (taken from this tutorial: http://www.codeproject.com/KB/aspnet/MVCFlexigrid.aspx):
public static IQueryable<T> Like<T>(this IQueryable<T> source,
string propertyName, string keyword)
{
var type = typeof(T);
var property = type.GetProperty(propertyName);
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var constant = Expression.Constant("%" + keyword + "%");
var like = typeof(SqlMethods).GetMethod("Like",
new Type[] { typeof(string), typeof(string) });
MethodCallExpression methodExp =
Expression.Call(null, like, propertyAccess, constant);
Expression<Func<T, bool>> lambda =
Expression.Lambda<Func<T, bool>>(methodExp, parameter);
return source.Where(lambda);
}
With this extension method, I can simply do the following:
someList.Like("FirstName", "mike");
or
anotherList.Like("ProductName", "widget");
Is there an equivalent way to do this with Entity Framework?
Thanks in advance.
The SQL method PATINDEX provides the same functionality as LIKE. Therefore, you can use the SqlFunctions.PatIndex method.
.Where(x => SqlFunctions.PatIndex("%123%ABC", x.MySearchField) > 0)
or
var miSqlPatIndex = typeof(SqlFunctions).GetMethod(
"PatIndex",
BindingFlags.Public | BindingFlags.Static | BindingFlags.IgnoreCase,
null,
new Type[] { typeof(string), typeof(string) },
null);
expr = Expression.GreaterThan(
Expression.Call(
miSqlPatIndex,
new Expression[] { Expression.Constant("%123%ABC"), MySearchField }),
Expression.Convert(Expression.Constant(0), typeof(int?)));
You could consider looking at this:
http://naspinski.net/post/Universal-IQueryable-Search-Version-2-with-Reflection.aspx
I was able to find a good solution here: http://www.codeproject.com/KB/aspnet/AspNetMVCandJqGrid.aspx
It essentially uses the "Contains" method of the string class instead of the Like method of the SqlMethods class.
Expression condition = Expression.Call(memberAccess, typeof(string).GetMethod("Contains"), Expression.Constant(keyword));

Categories

Resources