IQueryable.Any wrapper. Method cannot be translated into a store expression - c#

I have this code:
public static bool ContainEx<T>(this IQueryable<T> query, System.Linq.Expressions.Expression<System.Func<T, bool>> expression)
{
return query.Any(expression);
}
If I use that:
return bonusesWhereSearch.WhereEx(x => userBonusesWhereSearch.ContainEx(y => y.Bonus_Id == x.Id));
I get this error message:
System.NotSupportedException: LINQ to Entities does not recognize the
method 'Boolean
ContainEx[Bonus](System.Linq.IQueryable`1[SDataEntities.Bonus],
System.Linq.Expressions.Expression`1[System.Func`2[SDataEntities.Bonus,System.Boolean]])'
method, and this method cannot be translated into a store expression.
and if I use Any:
return bonusesWhereSearch.WhereEx(x => userBonusesWhereSearch.Any(y => y.Bonus_Id == x.Id));
that does work.

Problem here is, that entity framework doesn't execute ContainEx, but tries to translate this method into SQL. And since this is custom method translation fails. If you use Any directly, it is correctly translated to SQL equivalent.

Related

Move a lambda expression to a static extension method in Entity Framework

I am trying to refactor the following line of an Entity Framework query into a generalized static extension method:
dbContext.Employees
.Where(e => permissionResolver.AuthorizedUsers.Select(p => p.Id).Contains(e.Id))
.OrderBy(...)
PermissionResolver is just an instance I receive a list of IDs from to compare against a user ID stored in the current record. It compiles perfectly to a SQL statement WHERE Id IN (....).
Now I am trying to create an extension method for IQueryable<T> that I can use for any type of record, I just want to pass in a property where the owner's ID is stored in.
So that is what I came up with:
public static IQueryable<T> AuthorizedRecords<T>(this IQueryable<T> query, Expression<Func<T, Int32>> property, IPermissionResolver permissionResolver)
{
Expression<Func<T, Boolean>> idIsAuthorized = entity => permissionResolver.AuthorizedUsers.Select(e => e.Id).ToList().Contains(property.Compile()(entity));
return query.Where(idIsAuthorized);
}
I'm getting a runtime error that this expression cannot be translated into SQL.
How can I combine the property expression to the main query expression that it can be translated into SQL correctly? Is there a better way to rewrite the query expression?
property.Compile() converts the expression tree into a delegate, this delegate cannot be properly translated back to an expression tree/SQL.
You need to construct expression tree like that:
var ids = permissionResolver.AuthorizedUsers.Select(e => e.Id).ToList().AsEnumerable();
// method Enumerable.Contains<int>()
var methodContains = typeof(System.Linq.Enumerable).GetMethods()
.Where(m => m.Name == "Contains" && m.GetParameters().Length == 2)
.First()
.MakeGenericMethod(typeof(int));
var lambdaParam = property.Parameters.Single();
var lambda = Expression.Lambda(
Expression.Call(
methodContains,
Expression.Constant(ids),
property.Body),
lambdaParam
);
var predicate = (Expression<Func<T, bool>>)lambda;
return query.Where(predicate);
The short answer is you cannot use custom extension methods in entity framework queries. Under the hood entity framework parses expression tree Expression<Func<>> into sql query. It seems kind of impossible to translate any possible extension method to sql so they support limited set of Linq methods to properly translate it. Some useful information about expression trees: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/expression-trees.
Some refactoring you can do to simplify your query is to use Any method like this:
dbContext.Employees
.Where(e => permissionResolver.AuthorizedUsers.Any(u => u.Id == e.Id))
.OrderBy(...

How to add combined columns in linq where clause?

My query is like this:
var list = context.Items
.Where(i => i.Title.StartsWith(searchValue) ||
(i.Title + string.format("{0}prep", i.OrderNumber))
.StartsWith(searchValue))
.ToList();
But I am gettings exception - object not set to the instance of the object.
I also tried to add .AsEnumerable after .Where but doesn't work.
Without AsEnumerable I am getting:
LINQ to Entities does not recognize the method 'System.String
Format(System.String, System.Object)' method, and this method cannot
be translated into a store expression.
What I did wrong here?
What I did wrong here?
The answer is in the exception message:
LINQ to Entities does not recognize the method System.String Format(System.String, System.Object) method, and this method cannot be translated into a store expression.
In other words, string.Format method is not supported because it cannot be translated to SQL query.
Fortunately string concatenation is supported, so you can use this instead:
var list = context.Items.Where(i => i.Title.StartsWith(searchValue)
|| (i.Title + i.OrderNumber + "prep").StartsWith(searchValue))
.ToList();

LINQ to Entities: queryable extension method not reconized inside where condition

i am using asp.net 4.5 mvc 5 with code first entity framework and have the following issue:
i have two models, "PostBody" and "PostHeader". 1 PostHeader has 1-n PostBodies.
A PostBody can be "Deleted" (Property as flag).
My extension method should give me every PostBody from an IQueryable-Object which is not deleted as an IQueryable:
public static IQueryable<TSource> GetActiveVersions<TSource>(this IQueryable<TSource> source)
where TSource : PostBody
{
return source.Where(x => x.Deleted == false);
}
this is working fine when i do somethin like this
var x = db.Bodies.GetActiveVersions().ToList();
or this
var y = db.Headers.FirstOrDefault().Bodies.AsQueryable().GetActiveVersions().ToList();
etc. - But as soon as i use my extension method as part of an expression paramater within e.g. the where method, i'm running into a NotSupportedException:
var z = db.Headers.Where(h => h.Bodies.AsQueryable().GetActiveVersions().Count() > 0).ToList();
System.NotSupportedException: LINQ to Entities does not recognize the method 'System.Linq.IQueryable`1[WebApplication5.Models.PostBody] GetActiveVersions[PostBody](System.Linq.IQueryable`1[WebApplication5.Models.PostBody])' method, and this method cannot be translated into a store expression.
what am i doing wrong? or - how can i use my extension method within the where condition?
Maybe the parser cannot resolve the extension method in the linq expression. You could try to centralize your condition and use it as a expression tree, for sample:
public static Expression<Func<TSource, bool>> GetActiveVersions()
where TSource : PostBody
{
return x => x.Deleted == false;
}
And apply this expressin in your subquery (with linq) for sample:
var z = db.Headers.Where(h => h.Bodies.AsQueryable().Any(GetActiveVersions())).ToList();
Instead using the Count() > 0, prefer using .Any() to avoid to access all records of the table.

Error in a predicate with expression function

I'm using an expression function for predicate in LINQ Where method (that returns IQueryable result) which takes a class parameter:
public ActionResult FilterProfiles(FilterViewModel filter)
{
var profiles = database.Profiles
.Where(Predicate(filter))
...
}
The predicate is:
private static Expression<Func<UserProfile, bool>> Predicate(FilterViewModel f)
{
return p => (CompareFilter(p, f));
}
The problem is with the CompareFilter function:
private static bool CompareFilter(UserProfile profile, FilterViewModel filter)
{
if (filter.FirstName != null)
{
if (profile.FirstName != null)
{
if (profile.FirstName.CompareTo(filter.FirstName) != 0)
{
return false;
}
}
...
}
The exception is:
LINQ to Entities does not recognize the method 'Boolean CompareFilter(Project.Models.UserProfile, Project.Web.ViewModels.FilterViewModel)' method, and this method cannot be translated into a store expression.
Exception Details: System.NotSupportedException: LINQ to Entities does not recognize the method 'Boolean CompareFilter(Project.Models.UserProfile, Project.Web.ViewModels.FilterViewModel)' method, and this method cannot be translated into a store expression.
Thanks in advance!
The issue is that you're attempting to use a method in the lambda being translated by Entity Framework.
Entity Framework is actually using IQueryables to translate your method calls into SQL as you call them, to make querying a database seamless. This means, however, that any method you define is inherently outside the purview of Entity Framework, and it therefore cannot translate it into SQL, and thus fails. Even though you know the source, the Expression functions cannot access a compiled method as an expression tree.
A workarounf for this (and many other similar situations) is to use .AsEnumerable() which will cause further Linq calls to be the IEnumerable versions, which enumerate via foreach and thus pull our objects into memory.

The LINQ expression node type 'Lambda' is not supported in LINQ to Entities

I'm trying use Expression and pass to query. But I have error - The LINQ expression node type 'Lambda' is not supported in LINQ to Entities. I'm also used linqkit.dll with AsExpandable(), but but have the same error.
public List<Correct> GetCorrects(Expression<Func<Correct, bool?>> predicate)
{
using (SystemsEntities context = new SystemsEntities())
{
var result = context.Corrects.Where(x => predicate == null);
return result.ToList();
}
}
I get the error stated above. What is failing?
Use this:
var result = context.Corrects.Where(predicate);
instead of this:
var result = context.Corrects.Where(x => predicate == null);
Where expects argument of type Expression<Func<T, bool>>, but you're trying to pass Expression<Func<T, Expression<...>> instead. This is valid compile-time construct, but at run-time LINQ provider fails, when it tries to translate predicate to SQL.
Also note, that you should change Expression<Func<Correct, bool?>> into Expression<Func<Correct, bool>>.

Categories