C# Linq no working with "Greater than" operators - c#

Every time I try to run the following sentence in my function:
var comission = await _context.Comissions.FirstOrDefaultAsync(c =>
c.CountryReceiverId == receiverCountryId &&
c.CountrySenderId == senderCountryId);
var comissionRange = await _context.ComissionRanges
.Where(c => c.MinAmount <= amount && c.MaxAmount >= amount)
.FirstOrDefaultAsync(c => c.ComissionId == comission.Id);
it gives me the following error:
The LINQ expression 'DbSet<ComissionRange> .Where(c => c.MinAmount <= __amount_0 && c.MaxAmount >= __amount_0)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync().
The error happens at the comissionRange query specifically, and I noticed the following, like if the problem it is with the >= or <= operator, for example if I modify such query like this:
var comissionRange = await _context.ComissionRanges
.Where(c => c.MinAmount == 501 && c.MaxAmount == 1000)
.FirstOrDefaultAsync(c => c.ComissionId == comission.Id);
it works perfectly! It's like if the problem it's because I am using the >= and <= operators, what will be the right way to rewrite this query and make it work?

Related

Could not be translated. Either rewrite the query in a form that can be translated,

I have C# application (.NET Core 6) and I have written the following LINQ expression.
var data = query.OrderByDescending(a => a.CreatedOn).GroupBy(b => new { b.PackageID, b.PatientId }).ToList();
I get following error
The LINQ expression 'DbSet<LAB_ValueBasedResult>()
.Where(item => item.GroupId == 58)
.Where(item => item.HospitalId == 59)
.Where(x => x.IsActive)
.Where(x => x.CreatedOn >= __AddDays_0)
.Where(x => x.CreatedOn <= __AddDays_1)
.Where(x => __lstPatientID_2.Contains(x.PatientId))
.OrderByDescending(a => a.CreatedOn)
.GroupBy(b => new {
PackageID = b.PackageID,
PatientId = b.PatientId
})' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'.
I want to Add OrderByDescending and GroupBy in LINQ execute. how could I do it?
Please advise how can I resolve this.
Thank you.

Find in Entity Framework multiple OR parameters

Suppose I have a product in my database with the description “white shirt size 50”.
The search parameter would be “shirt 50”. I have a more complex query in which I add several “OR”s and I can't get them to work.
I get the following error:
The LINQ expression
'DbSet()
.Where(p => p.IdTienda == __request_IdTienda_0)
.Join(
inner: DbSet(),
outerKeySelector: p => p.IdArticulo,
innerKeySelector: a => a.Id,
resultSelector: (p, a) => new TransparentIdentifier<Publicacion, Articulo>(
Outer = p,
Inner = a
))
.Where(ti => __arrayrequest_1
.Any(s => ti.Outer.Descripcion.Contains(s)) || ti.Outer.Codigo == __request_Filtro_SearchText_2 || ti.Inner.Codigo == __request_Filtro_SearchText_2 || ti.Inner.CodigoUniversal == __request_Filtro_SearchText_2 || ti.Inner.CodigoUniversalBulto == __request_Filtro_SearchText_2)'
could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
My code so far is the following:
var arrayrequest = request.Filtro.SearchText.Split().ToList();
var query = from publicacion in _dbContext.Publicaciones.Where(p => p.IdTienda == request.IdTienda)
join articulo in _dbContext.Articulos
on publicacion.IdArticulo equals articulo.Id
where
arrayrequest.Any(s => publicacion.Descripcion.Contains(s))
|| publicacion.Codigo == request.Filtro.SearchText
|| articulo.Codigo == request.Filtro.SearchText
|| articulo.CodigoUniversal == request.Filtro.SearchText
|| articulo.CodigoUniversalBulto == request.Filtro.SearchText
select publicacion;
var publicaciones = await query
.Include(p => p.Articulo)
.Include(p => p.TributoPublicacion)
.ToArrayAsync();
The error occurs in the section
arrayrequest.Any(s => publicacion.Descripcion.Contains(s))`
I use Entity Framework Core 5 - any help is welcome
Don't want to repeat myself, but it is good to show how it can be solved.
EF do not supports complex predicates with local collections and here you need to build expression tree dynamically. This answer has GetItemsPredicate function which helps in building needed condition.
Then you can rewrite your query in this way:
var arrayrequest = request.Filtro.SearchText.Split().ToList();
var query = from publicacion in _dbContext.Publicaciones.Where(p => p.IdTienda == request.IdTienda)
join articulo in _dbContext.Articulos
on publicacion.IdArticulo equals articulo.Id
select publicacion;
var descriptionPredicate = query.GetItemsPredicate(arrayrequest, (publicacion, s) => publicacion.Descripcion.Contains(s));
Expression<Func<Publicacion, bool>> otherPredicate = publicacion => publicacion.Codigo == request.Filtro.SearchText
|| articulo.Codigo == request.Filtro.SearchText
|| articulo.CodigoUniversal == request.Filtro.SearchText
|| articulo.CodigoUniversalBulto == request.Filtro.SearchText;
query = query.Where(descriptionPredicate.CombineOr(otherPredicate)));
var publicaciones = await query
.Include(p => p.Articulo)
.Include(p => p.TributoPublicacion)
.ToArrayAsync();

Compare between DateTime from Client and DateTime Saved in Database

I'm trying to make a comparison between two Dates in Date,Hours,Minutes,Seconds but when I invoke
x.CreationTime.TimeOfDay the nullable exception appeared.
I thought this link answer will solve my problem but after I fellow the solution the problem still appread
Here is my query :
public async Task<List<MessageDto>> getMessageHistory(long userId, string code, long HCSId, long Role,DateTime latestMessageDateTime , DateTime messageDateBeforeSeeMore)
{
var result =await _repository.GetAllIncluding(x => x.listOfMessages)
.Where(x => ((x.receiverID == userId|| x.CreatorUserId == userId)
&& x.code== code) && (
x.CreationTime != null
&& x.CreationTime.Date > latestMessageDateTime.Date
&& x.CreationTime.TimeOfDay !=null
&& x.CreationTime.TimeOfDay.Hours > latestMessageDateTime.TimeOfDay.Hours /*<===== this cause the problem if I remove it the query working fine*/
)
&& x.listOfMessages.Any(x => x.HCSId== HCSId)
).OrderBy(message => message.CreationTime).ToListAsync();
return result ;
}
Update:
the Exception details :
- $exception {"Object reference not set to an instance of an object."} System.NullReferenceException
+ Data {System.Collections.ListDictionaryInternal} System.Collections.IDictionary {System.Collections.ListDictionaryInternal}
at MyProject.ChatAppService.MessageAppService.<getMessageHistory>d__19.MoveNext() in D:\WorkSpace\MyProject\aspnet-core\src\MyProject.Application\ChatAppService\MessageAppService.cs:line 346
+ TargetSite {Void MoveNext()} System.Reflection.MethodBase {System.Reflection.RuntimeMethodInfo}
Update 2:
I follow the steps that have been mentioned by #RandRandom
and what I found is below Exception
The LINQ expression 'DbSet<Message>
.Where(m => m.receiverID == __citizenId_0 || m.CreatorUserId == __userId_0 && m.code == __code_1 && DbSet<HCSMessages>
.Where(h => EF.Property<Nullable<long>>(m, "Id") != null && EF.Property<Nullable<long>>(m, "Id") == EF.Property<Nullable<long>>(h, "messageId"))
.Any(h => h.HCsId == __HCSId_2) && m.CreationTime > __messageDateBeforeSeeMore_3)
.Where(m => m.CreationTime.Date > __latestMessageDateTime_Date_4)
.Where(m => m.CreationTime.TimeOfDay.Hours > __latestMessageDateTime_TimeOfDay_Hours_5)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
Question:
The exception didn't have an inner exception with more usefull information?
Attempt of an answer:
A wild guess would be, the only thing that actually can be null at all is x.listOfMessages since it is the only reference type you are using, everything else is a struct.
You can "easily" narrow the error down, by spliting your query into multiple parts and materialize each query seperatly one after the other.
So first rewrite your query like this:
public async Task<List<MessageDto>> getMessageHistory(long userId, string code, long HCSId, long Role,DateTime latestMessageDateTime , DateTime messageDateBeforeSeeMore)
{
IEnumerable<MessageDto> result = await _repository.GetAllIncluding(x => x.listOfMessages);
result = result.Where(x => x.receiverID == userId || x.CreatorUserId == userId);
result = result.Where(x => x.code == code);
//dropped the null checks for DateTime and TimeOfDay, since NULL should be impossible
result = result.Where(x => x.CreationTime.Date > latestMessageDateTime.Date );
result = result.Where(x => x.CreationTime.TimeOfDay.Hours > latestMessageDateTime.TimeOfDay.Hours);
result = result.Where(x => x.listOfMessages.Any(x => x.HCSId== HCSId));
result = result.OrderBy(message => message.CreationTime);
return (List<MessageDto>)result;
}
After this add .ToListAsync() on each line beginning at the top and moving it with each successfull step one place further down.
So on your first test change the first line to this:
IEnumerable<MessageDto> result = await _repository.GetAllIncluding(x => x.listOfMessages).ToListAsync(); //added .ToListAsync()
For the second test remove the ToListAsync() from the first line and add it to the second line, first and second line should than look like this:
IEnumerable<MessageDto> result = await _repository.GetAllIncluding(x => x.listOfMessages); //removed .ToListAsync()
result = result.Where(x => (x.receiverID == userId || x.CreatorUserId == userId)).ToListAsync(); //added .ToListAsync()
With this approach your are materializing each condidition seperatly and can figure out what condition will fail.
Edit:
The error in "Update2" happens because you are doing something that can't be translated into a SQL query.
To fix this in general you have two options
Run the unsupported expression locally, there for skip the necessety to translate the expression to SQL
To do this you have to evaluate all expressions prior to the unsupported, the evalution happens when an enumeration on the IQueryable happens, easy ways to do this are for example to call ToList(), ToArray() or AsEnumerable(), after this your expression will run against an IEnumerable<T> instead of an IQueryable<T>
Use DbFunctions if a suiting one is available - https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.dbfunctions - or write your own functions - https://khalidabuhakmeh.com/add-custom-database-functions-for-entity-framework-core
But in your case I would question your expressions at a whole, why are comparing the date and hours seperatly
result = result.Where(x => x.CreationTime.Date > latestMessageDateTime.Date );
result = result.Where(x => x.CreationTime.TimeOfDay.Hours > latestMessageDateTime.TimeOfDay.Hours);
Instead of just simple comparing the DateTime struct?
result = result.Where(x => x.CreationTime > latestMessageDateTime );
At the beggining I will try to put the query parameters (date from client) outside the query and get concrete values.
For example
int lastMessageHours = latestMessageDateTime.TimeOfDay.Hours;
DateTime lastMessageDate = latestMessageDateTime.Date;
And then pass it to query
var result =await _repository.GetAllIncluding(x => x.listOfMessages)
.Where(x => ((x.receiverID == userId|| x.CreatorUserId == userId)
&& x.code== code) && (
x.CreationTime != null
&& x.CreationTime.Date > lastMessageDate
&& x.CreationTime.TimeOfDay !=null
&& x.CreationTime.TimeOfDay.Hours > latestMessageHours
)
&& x.listOfMessages.Any(x => x.HCSId== HCSId)
).OrderBy(message => message.CreationTime).ToListAsync();
If this does not help CreationTime has value if it is nullable type.
x.CreationTime.HasValue
I also suggest to split this query between multiple methods. Write extension method for this query:
public static IQueryable<Message> NewerThan(this IQueryable<Message> query, DateTime value)
{
int lastMessageHours = latestMessageDateTime.TimeOfDay.Hours;
DateTime lastMessageDate = latestMessageDateTime.Date;
return query.Where(x=> x.CreationTime != null
&& x.CreationTime.Date > lastMessageDate
&& x.CreationTime.TimeOfDay !=null
&& x.CreationTime.TimeOfDay.Hours > latestMessageHours );
}
Query will be more readable.

Conditioning LINQ Query

Currently I have a LINQ query like this:
var policy = snapshotDate == null
? await _dbContext.VPolicies
.Where(p => p.PolicyNumber.Trim().Equals(policyNumber))
.OrderByDescending(d => d.PolicyEffectiveDate)
.FirstOrDefaultAsync()
: await _dbContext.VPolicies
.Where(p => p.PolicyNumber.Trim().Equals(policyNumber)
&& (p.PolicyEffectiveDate <= snapshotDate && p.PolicyExpirationDate > snapshotDate))
.OrderByDescending(d => d.PolicyEffectiveDate)
.FirstOrDefaultAsync();
I would like to know if I can shorten it like this:
var policy = await _dbContext.VPolicies
.Where(p => p.PolicyNumber.Trim().Equals(policyNumber))
.Where(p => snapshotDate != null && (p.PolicyEffectiveDate <= snapshotDate && p.PolicyExpirationDate > snapshotDate))
.OrderByDescending(d => d.PolicyEffectiveDate)
.FirstOrDefaultAsync();
But this doesn't work and I would like to know how I can condition this LINQ query in a proper way and shorten them without using the terenary operator.
Thanks in advance.
Try this:
var policy = await _dbContext.VPolicies
.Where(p => p.PolicyNumber.Trim().Equals(policyNumber)
&& (snapshotDate == null ||
(p.PolicyEffectiveDate <= snapshotDate &&
p.PolicyExpirationDate > snapshotDate)))
.OrderByDescending(d => d.PolicyEffectiveDate)
.FirstOrDefaultAsync();
This query will apply conditions of PolicyEffectiveDate and PolicyExpirationDate if snapshotDate is not null otherwise only PolicyNumber condition will apply.
Seems like you want your condition to evaluate
snapshotDate == null || (p.PolicyEffectiveDate <= snapshotDate && p.PolicyExpirationDate > snapshotDate)
Which says, give all dates when snapshotDate is null, or within when it's not.
Note : about multiple where clauses. In a memory based collection you are will get a performance increase combining them with an && (due to the extra delegate invocation, and potential multiple enumerations), however with most LINQ to Query providers it will evaluate in the exact same SQL. Though in general it's best combining these if you can.

Linq To Entities Internal Conversion

I must to execute this expression:
var CAP = modelCap.AZCPC00F.Where(x => x.CPCVER == CPCVER && x.CPCNAR == CPCNAR && x.CPCCAP == CPCCAP)
.Where(x => XXXXKG == 0 ? true : Convert.ToInt64(Convert.ToDouble(x.CPCLKG)) < XXXXKG)
.Where(x => XXXXMC == 0 ? true : Convert.ToInt64(Convert.ToDouble(x.CPCLMC)) < XXXXMC)
.Where(x => XXXXFD == "N" ? true : x.CPCZFD == XXXXFD).FirstOrDefault();
When I try to execute this I have an exception on the internal conversion of x.CPCLKG. The exception is:
LINQ to Entities does not recognize the method 'int64' method, and this method cannot be translated into a store expression.
I know that the problem is in the conversion but how can i use this function?
An example of x.CPCLKG is 9.9999 and is an nChar character type.
Thanks to all
You need to have the logic that is not supported in Linq To Entities done in an in-memory query. We can do this by calling ToList() on the parts of the query that are supported. This will execute those parts, and return the results as a list. We can then execute whatever Linq supports on that list, in memory.
var CAP = modelCap.AZCPC00F.Where(x => x.CPCVER == CPCVER && x.CPCNAR == CPCNAR && x.CPCCAP == CPCCAP)
.Where(x => XXXXFD == "N" ? true : x.CPCZFD == XXXXFD)
.ToList();
var CAP2 = CAP.Where(x => XXXXKG == 0 ? true : Convert.ToInt64(Convert.ToDouble(x.CPCLKG)) < XXXXKG)
.Where(x => XXXXMC == 0 ? true : Convert.ToInt64(Convert.ToDouble(x.CPCLMC)) < XXXXMC).FirstOrDefault()

Categories

Resources