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.
Related
I'm trying to query my address location to find out addresses which are within the specific radius. Below is my where clause and results is an IEnumerable<Properties>
Func<Address, bool> predicate = l =>
{
if (l.Location == null && l.Longitude.HasValue && l.Latitude.HasValue)
{
var point = new Point(l.Longitude.Value, l.Latitude.Value);
return point.Distance(p) <= 300;
}
return false;
};
results = results.Where(x => predicate(x.Address));
However, I'm getting an exception from NHibernate as
"Unable to cast object of type 'NHibernate.Hql.Ast.HqlParameter' to type 'NHibernate.Hql.Ast.HqlBooleanExpression'.
How can I fix this?
Any help is highly appreciated.
You need to use LINQ expressions, not lambdas!
Try this instead:
Expression<Func<Address, bool>> predicate = ...
Your filtering expression cannot be translated to SQL. It is a multi-line expression. linq-to-nhibernate, as any other orm LINQ provider, requires simple one liner lambda that can be translated to SQL.
Furthermore, your filtering expression use a class performing some computation (Distance() in your case). The LINQ provider will have no knowledge of how to convert that to SQL, unless you extend it. Better rewrite your computing in the lambda, if it can be written in a SQL translatable way.
If you need your predicate in many places, you may write a function to get it in a local var before using it.
private Expression<Func<Address, bool>> GetFilterByMaxDistance(double maxDistance)
{
// replace ??? with your computation, without block of code (no `{}`) nor call to
// functions not known for being handled by the LINQ provider
return a => ??? <= maxDistance;
}
...
var predicate = GetFilterByMaxDistance(300);
results = results.Where(predicate);
Otherwise, if your results local variable is already 'sufficiently filtered' for having reasonable performances, call .AsEnumerable() before applying your filter, this time only as a Func<Address, bool>, not as an Expression<Func<Address, bool>>.
Calling .AsENumerable() before applying .Where(predicate) will cause your predicate to get executed in memory by .Net runtime, without the need to convert it to SQL.
But of course this will cause the filtering to occur in your app rather than in the DB.
I'm getting this error when trying to do a linq query:
LINQ to Entities does not recognize the method 'System.Collections.Generic.IEnumerable`1[FEI.Entities.EF6.Tables.HORSE_IDENTITY_GENDER] GetHorseIdentityGenderQuery(FEI.Entities.EF6.Tables.HORSE_DOCUMENT_PART)' method, and this
method cannot be translated into a store expression.
I've read a lots of previous questions where people get the same error, but I understand that it's because LINQ to Entities requires the whole linq query expression to be translated to a server query, and therefore you can't call an outside method in it. I haven't been able to convert my scenario into something that works yet, and my brain is starting to melt down, so I was hoping someone could point me in the right direction. We're using Entity Framework and the specification pattern (and I'm new to both).
Here's the code that uses the specification:
HORSE_DOCUMENT HorseDocForPart = Bll.GetHorseDocumentForPartUpload(horseId, identityType);
Here's the code that provides from method GetHorseDocumentForPartUpload
public HORSE_DOCUMENT GetHorseDocumentForPartUpload(int horseID, HorseDocGender identityType)
{
// Get the unique horse document
var horseDoc = Tables
.HORSE_DOCUMENT
.Where(hd =>
hd.HORSE_UID == horseID &&
hd.HORSE_IDENTITY_TYPE.HORSE_IDENTITY_TYPE_FULL_CODE == identityType.ToString() &&
!hd
.HORSE_DOCUMENT_PART
.Any(hdp =>
hdp.VALIDATION_STATUS != HorseDocPartStatus.REFUSED.ToString() &&
GetHorseIdentityGenderQuery(hdp).Any(hig => hig.IS_FULL)
)
).SingleOrDefault();
return horseDoc;
}
Here's the last code :
public IEnumerable<HORSE_IDENTITY_GENDER> GetHorseIdentityGenderQuery(HORSE_DOCUMENT_PART horseDocPart)
{
var possibleDocs = Tables
.DOCUMENTs
.Where(doc => doc.DOC_OWNER_UID == horseDocPart.HORSE_DOCUMENT_PART_UID);
return horseDocPart
.HORSE_DOCUMENT
.HORSE_IDENTITY_TYPE
.HORSE_IDENTITY_GENDER
.Join(
possibleDocs,
hig => hig.DOCUMENT_GENDER_CODE.DOCUMENT_GENDER_CODE_UID,
doc => doc.DOCUMENT_GENDER_CODE_UID,
(dgc, doc) => dgc
);
}
You return IEnumerable from the method
public IEnumerable<HORSE_IDENTITY_GENDER> GetHorseIdentityGenderQuery(...)
This is deferred but using IEnumerable does not allow Linq-To-Sql execution, you should be using IQueryable as such.
public IQueryable<HORSE_IDENTITY_GENDER> GetHorseIdentityGenderQuery(...)
Please see more detailed explanation from Returning IEnumerable<T> vs. IQueryable<T>
I'm currently implementing the repository pattern for my ASP.VNext application. I would like the methods to be asynchronous and filterable. So I have devised the following interface method:
Task<TEntity> GetOneAsync(Func<TEntity,bool> predicate);
and would like to implement it like this (with a private DbContext instance ctx):
public async Task<MyEntity> GetOneAsync(Func<MyEntity,bool> predicate)
{
// compiler error
return await ctx.MyEntities.Where(predicate).FirstOrDefaultAsync();
}
However I can only use FirstOrDefaultAsync() when hardcoding the predicate like this:
return await ctx.MyEntites.Where(e => e.Id == 1).FirstOrDefaultAsync();
When passing the predicate i only get the FirstOrDefault() without the async option, so in order to make my method asynchronous I have to write
public async Task<MyEntity> GetOneAsync(Func<MyEntity,bool> predicate)
{
//save to a local variable to prevent calling a disposed DbContext
var entities = await Task.Run(() => ctx.Contracts.Where(predicate).FirstOrDefault());
return entities;
}
I have two questions regarding this:
Why is it not possible to access the FirstOrDefaultAsync() method when passing a predicate?
Does my solution using await Task.Run(synchronousMethod) achieve the same behavior as a call to FirstOrDefaultAsync() would?
FirstOrDefaultAsync is defined as an extension method for IQueryable<T>.
ctx.MyEntities.Where(e => e.Id == 1) returns IQueryable<MyEntity>.
ctx.MyEntities.Where(predicate) returns IEnumerable<MyEntity>, because you're calling the Enumerable.Where extension method, not the Queryable.Where one.
To make it work, change predicate from Func<MyEntity, bool> to Expression<Func<MyEntity, bool>>. This means predicate is no longer just a function that gives the result you want, but a description of that function, that Entity Framework can then translate to SQL.
And no, using Func<MyEntity, bool> within a task would not have the same behaviour. That would load rows from the db server without any filtering, and evaluate each and every one at the db client until a match is found. That would add a lot of overhead.
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.
Is it possible to execute a method with in Linq ie
var lst = (from ls in testEntity.Month where ls .Month1.ToString() == hello() select ls).ToList();
private string hello()
{
return "8";
}
I know that Linq will not execute itself, while executing this i am getting the following error
LINQ to Entities does not recognize the method 'System.String ToString()' and this method cannot be translated into a store expression.
Generally, you can call methods in LINQ without problems.
The problem you are encountering here is specific to LINQ to Entities. L2E needs to translate all your method calls into the appropriate database statements, and for the method you called it doesn't know how to translate it.
You could rewrite your hello() function to return an expression:
public Expression<Func<string>> hello() {
return () => "8";
}
that way L2E can translate the statements. If that works, depends on your real code, of course.