How to check if column value has "value" - c#

I want to check if value of "Acceptance" is "Accepted" and if it has that value- .Sum else i want to get 0, but have some problms with finding correct syntax. I cant get "Acceptance" value
My method
public decimal Prices2(string term)
{
var kl= db.Order.Where(x => x.Client == term && x.Acceptance == "Accepted");
if (term != null && kl.Acceptance == "Accepted" )
{ ^^//Element does not contain definition"Acceptance"
return kl.Sum(x => x.Price);
}
else
{
return 0;
}
}
how I get "term"
public IEnumerable<string> GetClient(string term)
{
return db.Order.Where(x => (term != null && x.Client.Contains(term)) || (term == null)).OrderBy(c => c.Client).Take(10).Select(a => a.Client).Distinct();
}
When i try this:
return db.Order.Where(x => x.Client == term && x.Acceptance == "Accepted").sum(x=>x.Price)
I get error when there is no "Accepted" order for client
System.InvalidOperationException: „The cast to value type 'System.Decimal' failed because the materialized value is null. Either the result type's generic parameter or the query must use a nullable type.”
How can i check first if there is Accepted order for client and return 0 if there istn?

First Assign the query to one variable,
var orders = db.Order.Where(x => x.Client == term && x.Acceptance == "Accepted");
var result= orders.Count() > 0 ? orders.Sum(x=>x.Price) : 0.0M;
return result;
You're return type of method should be decimal
.

First of all, your code will not work because variable kl is of type IEnumerable<Order> which doesn't have a property Acceptance.
The code mentioned by #uk2k05 in the comments section will solve your problem:
return db.Order
.Where(x => x.Client == term && x.Acceptance == "Accepted")
.Sum(x => x.Price);
But another problem will occur, that's because Sum method returns Nullable<Decimal>, while your method Prices2 returns decimal. Click to see docs
To solve this problem you can use the following code instead:
return db.Order
.Where(x => x.Client == term && x.Acceptance == "Accepted")
.Sum(x => x.Price) ?? 0;
That will return the value returned by Sum or 0 if the returned value is null.

Related

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.

Max throws InvalidOperationException

I Have this query
decimal? query = context.History
.Where(x => x.ItemId == Id
&& x.OtherFilter == IdOtherFilter
&& x.Fecha < Fecha)
.Select(x => x.Secuencia)
.Max();
I need to perform this query into Linq:
select max(Secuencia)
from History
where ItemId= 1406
and OtherFilter= 3
and Fecha < '20150922'
group by OtherFilter
I need to get the max Secuencia from a table that match with criteria, it works fine when criteria returns data, but when no data match with criteria, Max() can't materialize throwing an InvalidOperationException,
The cast to value type 'System.Decimal' failed because the materialized value is null. Either the result type's generic parameter or the query must use a nullable type.
how to avoid that ?
There is another Linq that I can use ?
are those sentences equivalent? if not What is wrong in my linq?
Assuming that the type of the Secuencia property is decimal and not decimal?, you should try adding a cast to decimal? so that Max will return null in the case that there are no values.
decimal? query = context.History
.Where(x => x.ItemId == Id
&& x.OtherFilter == IdOtherFilter
&& x.Fecha < Fecha)
.Select(x => (decimal?) x.Secuencia)
.Max();
or
decimal? query = context.History
.Where(x => x.ItemId == Id
&& x.OtherFilter == IdOtherFilter
&& x.Fecha < Fecha)
.Select(x => x.Secuencia)
.Cast<decimal?>()
.Max();
Make following modification to make it work, add DefaultIfEmpty() before Max() call, this will take care of empty list, also you may specify custom default value like 0.0, here it would be null, since the type selected is a nullable value type, check DefaultIfEmpty
decimal? query = context.History
.Where(x => x.ItemId == Id
&& x.OtherFilter == IdOtherFilter
&& x.Fecha < Fecha)
.Select(x => x.Secuencia)
.DefaultIfEmpty()
.Max();

how to correct the following linq?

I have the following LINQ statement:
var inspectorAllocSummary = context
.UserDetails
.Where(ud => ud.PerformAudit == true && ud.Valid == true)
.OrderBy(ud => ud.Firstname)
.Select(ud
=> new InspectorWorkStatusModel
{
InspectorName = ud.Firstname,
ToBeCompleted = context
.InspectorWorkAllocations
.Where(x => x.UploadCOESDetails.AuditMonth
== criteria.AuditMonth
&& x.InspectorId == ud.Id)
.Sum(x=> x.ToBeAudited) ?? 0,
Completed = context
.COESDetails
.Where(x => x.UploadCOESDetails.AuditMonth
== criteria.AuditMonth
&& x.InspectorId == ud.Id
&& x.AuditType != null)
.Count()
});
The ToBeCompleted is an integer, it gets data from the database, but if it is null I want to make sure it is set to 0. I tried the following:
ToBeCompleted = context
.InspectorWorkAllocations
.Where(x => x.UploadCOESDetails.AuditMonth == criteria.AuditMonth
&& x.InspectorId == ud.Id)
.Sum(x=> x.ToBeAudited) ?? 0
but I get the following error:
Operator '??' cannot be applied to operands of type 'int' and 'int'
How can I ensure that if the return data is null, that it will be set to zero?
If ToBeAudited is an int then Sum can never return null, it always returns an int. If the collection is empty it will return 0. just take off ?? 0.
However, Linq-to-SQL and Linq-to-Entities can throw an error if the resultant SQL does not return any records. For Linq-to-SQL one "fix" is to cast the underlying field to a nullable type:
.Sum(x=> (int?)x.ToBeAudited) ?? 0
For Linq-to-Entities a fix is to call .DefaultIfEmpty():
.Select(x=> x.ToBeAudited)
.DefaultIfEmpty(0)
.Sum()
First, you need to change the type of ToBeAudited to int?. The problem comes from the fact that int is a value type and it cannot contain null. That's why in your first approach you get the error:
The cast to value type 'Int32' failed because the materialized value is null.
There is a Sum overload which accepts nullable values. So after changin ToBeAudited to nullable this will suffice:
ToBeCompleted = context
.InspectorWorkAllocations
.Where(x => x.UploadCOESDetails.AuditMonth == criteria.AuditMonth
&& x.InspectorId == ud.Id)
.Sum(x => x.ToBeAudited).Value;
You don't need the ?? operator, as null values are being automatically ignored (and for the empty sequence Sum will return 0).

Order by child record value(s) that can be empty

I've tried some combinations but I just don't understand how to do the following:
Lets say I have tables Requests and RequestActivities. I need to get all request sorted by RequestActivity.TimeOfCreation in descending order but RequestActivity may be null.
List<DA.GeneralRequest> ongoingGeneralRequests = db.GeneralRequests
.Where(t => t.GeneralRequestStatusID != 3 && (t.SupervisorID == currentUserId || t.CreatorID == currentUserId || t.AssignedUsers.Any(au => au.UserID == currentUserId)))
.OrderByDescending(x => x.GeneralRequestActivities.OrderBy(ga => ga.GeneralRequestActivityDate).Last().GeneralRequestActivityDate) //gives exeption
.ThenBy(a => a.Deadline).ToList();
I'm not really familiar with LINQ-To-SQL but doesn't work MAX in this case?
.OrderByDescending(x => x.GeneralRequestActivities
.Max(ga => ga.GeneralRequestActivityDate))
.ThenBy(a => a.Deadline)
.ToList();
You need to first cache the ordering value, and then order by the date if it is not null, else by some default date you want:
List<DA.GeneralRequest> ongoingGeneralRequests = db.GeneralRequests
.Where(t => t.GeneralRequestStatusID != 3 && (t.SupervisorID == currentUserId || t.CreatorID == currentUserId || t.AssignedUsers.Any(au => au.UserID == currentUserId)))
.Select(x => new {
Value = x,
OrderByValue = x.GeneralRequestActivities
.OrderBy(ga => ga.GeneralRequestActivityDate)
.LastOrDefault()) // cache value
.OrderByDescending(x => x.OrderByValue != null ?
OrderByValue.GeneralRequestActivityDate
: some default value)
.ThenBy(a => a.Value.Deadline)
.Select(a => a.Value)
.ToList();
Note that you can't use Last() extension method on empty IEnumerable. This is why you get the exception:
InvalidOperationException : The source sequence is empty.
In this line:
x.GeneralRequestActivities.OrderBy(ga => ga.GeneralRequestActivityDate).Last()
x.GeneralRequestActivities is empty, so calling Last() on it result on the exception.
Instead, use the LastOrDefault() extension method, which return null if the IEnumerable is empty.
Return Value Type: TSource default (TSource) if the source sequence is
empty; otherwise, the last element in the IEnumerable.

Entity Framework: Unable to create a constant value of type 'System.Collections.Generic.IList`1'

This has caused me no end of problems today. I have this simple query
var result =
DataContext.Accommodations.Where(a =>
(criteria.MinPrice == null || a.AccommodationRates.Any(r => r.From >= criteria.MinPrice)) &&
(criteria.MaxPrice == null || a.AccommodationRates.Any(r => r.To <= criteria.MaxPrice)) &&
(criteria.Locations == null || criteria.Locations.Count == 0 || a.AccommodationPlaceJoins.Any(j => criteria.Locations.Contains(j.Place.PlaceName)))
);
The last line of this query is causing me problems
(criteria.Locations == null ||
criteria.Locations.Count == 0 ||
a.AccommodationPlaceJoins.Any(j => criteria.Locations.Contains(j.Place.PlaceName)))
The error it gives is
Unable to create a constant value of type
'System.Collections.Generic.IList`1'. Only primitive types ('such as
Int32, String, and Guid') are supported in this context.
I'm not even trying to create a list. All I'm trying to do here is bring back accommodations which are associated to a place (where the place name in the Place table which is linked to the Accommodation table via the AccommodationPlaceJoin table) is equal to any one of the place names in criteria.Locations (which is of type IList).
I've tried changing this line to this, but it didn't work.
(criteria.Locations == null ||
criteria.Locations.Count == 0 ||
a.AccommodationPlaceJoins.Any(j => criteria.Locations.Any(l => l == j.Place.PlaceName)))
The constant value EF can't create is null for the comparison criteria.Locations == null. You need to split the query into two cases and do the check for the empty list outside of the query, for example like so:
var result = DataContext.Accommodations.Where(a =>
(criteria.MinPrice == null ||
a.AccommodationRates.Any(r => r.From >= criteria.MinPrice)) &&
(criteria.MaxPrice == null ||
a.AccommodationRates.Any(r => r.To <= criteria.MaxPrice)));
if (criteria.Locations != null && criteria.Locations.Count > 0)
{
result = result.Where(a => a.AccommodationPlaceJoins
.Any(j => criteria.Locations.Contains(j.Place.PlaceName)));
}
Edit
BTW: Composing the whole query would make it better readable in my opinion and will simplify the SQL that has to be sent to the database:
IQueryable<Accommodation> result = DataContext.Accommodations;
if (criteria.MinPrice != null)
result = result.Where(a => a.AccommodationRates
.Any(r => r.From >= criteria.MinPrice));
if (criteria.MaxPrice != null)
result = result.Where(a => a.AccommodationRates
.Any(r => r.To <= criteria.MaxPrice));
if (criteria.Locations != null && criteria.Locations.Count > 0)
result = result.Where(a => a.AccommodationPlaceJoins
.Any(j => criteria.Locations.Contains(j.Place.PlaceName)));
I had this same issue when trying to pass a list of strings for comparison into an EF core query recently and found that it resolves the issue to assign an empty array or empty list to the value in case it is null rather than doing the null check right in the EF Core query.
This allows you to query the EF Core database without the null error and you don't need a bunch of conditional branching for separate queries - just one query with a bit of null coalescence.
So your code fixed this way would look like this:
var locations = criteria.Locations == null ? new List<Location>() : criteria.Locations;
var result =
DataContext.Accommodations.Where(a =>
(criteria.MinPrice == null || a.AccommodationRates.Any(r => r.From >= criteria.MinPrice)) &&
(criteria.MaxPrice == null || a.AccommodationRates.Any(r => r.To <= criteria.MaxPrice)) &&
(locations.Any() ? a.AccommodationPlaceJoins.Any(j => criteria.Locations.Contains(j.Place.PlaceName))) : true);
Keep in mind I am not sure of the type for locations or accomodationplacejoins so I cannot be sure the above code will exactly work but the general idea is we are doing a .Any() instead of checking for null. This way we can keep our query a little simpler. We are then using a ternary expression within the last block of our where clause so if the locations list turns out to be empty, we skip the condition entirely and just return true which coalesces the empty list if it is empty.

Categories

Resources