Method using LINQ in my Repository that gets null errors - c#

I have a Method in my LINQ
Basicly what my LINQ query does is that it retrieves data from my database.
A user can choose between two dates and retrieve the data between those two dates. But if a user choose on a date range which there is no data in I recieve this error:
The cast to value type 'Double' failed because the materialized value
is null. Either the result type's generic parameter or the query must
use a nullable type
It should be okey for the users to search between the two dates even if there is no data, it should give a error message that there is no data between those dates instead of this error I recieve in my Visual studio.
I am using MVC entity framework model first.
Here is the method:
public List<CoreValueAndAverageGrade> GetAverageGradeForAllCoreValues(
OfficeStatisticQueryViewModel model)
{
var StartDate = DateTime.Parse(model.StartDate);
var EndDate = DateTime.Parse(model.EndDate);
return db.CoreValue
.Where(v => v.CoreValueQuestion
.Any(q => !q.SubjectType.Ignored_Statistic))
.Select(coreValue => new CoreValueAndAverageGrade
{
CoreValue = coreValue,
AverageGrade = coreValue.CoreValueQuestion
.Where(q => !q.SubjectType.Ignored_Statistic)
.Average(q => q.SelectedQuestions
.Where(s =>
s.GoalCardQuestionAnswer != null
&& s.GoalCardQuestionAnswer.Grade.HasValue
&& s.GoalCard.Completed_Date >= StartDate
&& s.GoalCard.Completed_Date <= EndDate
)
.Average(s => s.GoalCardQuestionAnswer.Grade.Value))
})
.ToList();
}
Update: Grade is Double and Nullable
Any kind of help is very appreciated!
Thanks in advance!

Looking at the last part of the query - if there are no rows returned after filtering with the where clause, then s.GoalCardQuestionAnswer.Grade.Value will definitely throw because you're trying to access properties of an object that is null.
I suppose that if you change the code to this you would get 0 as the final value - then you'd need to check for this value explicitly in your code.
.Average(s => s != null ? s.GoalCardQuestionAnswer.Grade.Value : 0)
Otherwise, breaking the query down would be a good idea - it helps both debugging and readability of the code.

I would split up the queries and investigate further. My guess would be that there is some inconsistent data. Probably it happens somewhere where you compare a double with a double? and the double? value is null. Maybe check your records for nulls, which should actually be there.

That error usually comes up when you are trying to apply an aggregate operator to a result set with no records. Look at where you are using .Average and I am sure you have a query that returns no records right before.

Related

EF Core - Exclude from IQueryable if Datetime-Value is between 2 DateTimes from a list

I'm currently trying to achieve the following :
I have an IQueryable (UserDataDateRange) that has "from" and "to" DateTime-Values and another bigger IQueryable (UserData) with a DateTime-value and basically I want to exclude every data from userdata that Datetime-value is between the "from" and "to" comparing it to every UserDataDateRange-Entry.
Also every DateTime is nullable and I just want to ignore those.
Here is what I have tried :
private IQueryable<Userdata> ExcludeIfInDaterange(IQueryable<Userdata> query)
{
var dateRangeQuery = DBContext.UserDateDateRange.Where(x => x.From.HasValue && x.To.HasValue);
query = query.Where(l => !l.UserDate.HasValue);
foreach (var q in dateRangeQuery)
{
query = query.Where(l => l.UserDate.Value <= q.From.Value && l.UserDate.Value >= q.To.Value);
}
return query;
}
From my understanding this should work? Also I have tried avoid using something like "toArray" because from my understanding an IQueryable is basically the SQL that im manipulating and something toArray gives me the actual data.
However I really don't know what I'm doing wrong, theres no real exception, im just getting the following error :
Could not get function from a frame. The code is not available. The
error code is CORDBG_E_CODE_NOT_AVAILABLE, or0x80131309.
My function seems to break the query but i cant figure out why. I cant even use "Count()", it gives me the same error.
Anyone got an idea?
l.UserDate.Value <= q.From.Value && l.UserDate.Value >= q.To.Value
How can a date be both earlier than the From date and later than the To date at the same time? Unless you have some very odd data in your UserDateDateRange table, that filter will exclude all records.
You're also combining two mutually-exclusive filters with an AND operator, which is another way to exclude all records:
!l.UserDate.HasValue : UserDate is NULL
l.UserDate.Value <= ... : Can't possibly be satisfied, since UserDate is NULL
And there's no need to use .Value when comparing nullable properties with < / <= / > / >=.
Try something like:
private IQueryable<Userdata> ExcludeIfInDaterange(IQueryable<Userdata> query)
{
var dateRangeQuery = DBContext.UserDateDateRange.Where(x => x.From.HasValue && x.To.HasValue);
return query.Where(l => !l.UserDate.HasValue || !dateRangeQuery.Any(q => q.From <= l.UserDate && l.UserDate <= q.To));
}

How to get data from linq query using select

I'm testing a simple LINQ select query and want to get two DateTime values from the table, but I'm doing something wrong here and need to know what I'm doing/thinking wrong?
My query:
var test = from x in db.Projects where x.ID == 1 select x;
Then I try to get on of the values like this:
DateTime Date = test. ?????
Here I thought I should get a suggestion from the Intellisense after the dot to pick the value from the column StartDate the table, but this isn't working.
If you need multiple matches...
Are you sure that you have multiple Project objects that have the same ID of 1 which your query currently suggests? If that is the case, then your query should return all of the records that meet that constraint via the Where() method :
// Get your Projects that meet your criteria
var test = db.Projects.Where(p => p.ID == 1);
If you need to access properties from these elements, you could either loop through them explicitly :
// Iterate through each match that was found
foreach(var t in test)
{
// Access your properties here
DateTime dt = t.YourDateProperty;
}
Or you could accomplish this using a Select() statement to only pull the properties that you need :
// This will return a collection of Dates mapped from each element in your collection
var testDates = db.Projects.Where(p => p.ID == 1)
.Select(x => x.YourDateProperty);
If you only need a single match...
If you only need to match a single element within your collection, you might consider using the First(), Single() or their equivalent FirstOrDefault() and SingleOrDefault() methods, which will return a single entity that you can use as expected :
// This will return the first Project with an ID of 1
var test = db.Project.FirstOrDefault(p => p.ID == 1);
// If it was successful
if(test != null)
{
// Then you can safely access it here
DateTime dt = test.YourDateProperty;
}
The only difference between the methods mentioned (normal vs OrDefault()) is that the OrDefault() methods will return null if no matching elements are found, so they generally require a null check as seen above.
test is going to be an enumeration (IEnumerable<>, IQueryable<>, etc... many are applicable) of your Project type. So if, for example, you want the first record, you might do this:
DateTime Date = test.First().SomeDateProperty;
All of the data returned from your query is in test. It could be zero records, one record, many records, etc.
In test you will have a collection which matches the condition x.ID == 1. You should iterate through that collection and take your needed properties.
Edit
I suggest you to use the syntax:
var result = db.Projects.FirstOrDefault(x => x.ID ==1);
this is such as:
var date = test.FirstOrDefault();
DateTime? Date = date != null ? date.StartDate : null;

Truncate Time Converting list to nullable

public List<DateTime> CalorieDates(int patientId)
{
using (var db = new DbConn())
{
List<DateTime> query =
db.Calories.Where(d => d.PatientId == patientId && d.FoodId != "initial" && d.DateOfEntry != null)
.Select(d => System.Data.Entity.DbFunctions.TruncateTime(d.DateOfEntry)).Distinct().ToList();
return query;
}
}
Why is this converting my list to nullable datetimes?
Error CS0029 Cannot implicitly convert type
'System.Collections.Generic.List<System.DateTime?>'
to 'System.Collections.Generic.List<System.DateTime>'
How do I prevent this? I do not want a list of nullable datetimes.
For some unknown reason (probably to make the query providers life easier), DbFunctions class does not provide separate TruncateTime overloads for DateTime and DateTime?, but single method with DateTime? argument which handles both cases. However, as a side effect it's changing the expression type to DateTime? even if the argument is DateTime (as it seems to be in your case).
So you need to correct that. Once you know that you are always passing DateTime (hense the result cannot be null), you can simply add .Value at the end of the TruncateTime call and the issue is solved.
.Select(d => System.Data.Entity.DbFunctions.TruncateTime(d.DateOfEntry).Value)
.Select(d => System.Data.Entity.DbFunctions.TruncateTime(d.DateOfEntry))
.Distinct().ToList().Cast<List<DateTime>>()
Like #stuartd says in comments, TruncateTime is probably returning DateTime?; your Select returns IEnumerable<DateTime?> because that's what your lambda gives it from TruncateTime, Distinct() returns the same, and ToList() converts it into List<DateTime?> because, hey that's what IEnumerable<DateTime?>.ToList() returns.
So you end up with a list of DateTime?. That's easy. If you don't want that, Select something else.
Now, I'm going to assume that TruncateTime returns a nullabe type because it may return null. My guess is that its parameter is DateTime?, and it'll only return null if you pass in a null. Since you're passing in a struct, it probably can't return null in your particular usage of it, but I don't know so I'll play it safe.
So you want to convert your enumeration of nullable datetimes to regular ones, without casting any null nullables to regular DateTime. So first filter down to the ones that aren't null, then select their values.
.Select(d => System.Data.Entity.DbFunctions.TruncateTime(d.DateOfEntry))
.Where(d => d.HasValue)
.Select(d => d.Value)
.ToList();
It looks like the db method you are calling to truncate the datetime simply removes the time portion
If that is the case then use the following instead.
List<DateTime> query = db.Calories.Where(d => d.PatientId == patientId && d.FoodId != "initial" && d.DateOfEntry != null)
.Select(d => d.DateOfEntry.Date).Distinct().ToList();

Linq - between dates query

I am trying to convert the following sql query as a linq query, however I keep experiencing an error -
Operator '<=' cannot be applied to operands of type 'string' and 'System.DateTime'.
SQL Query:
select top 3 Deal, [property], [event], [Date] from [dbo]. [Database_CRE_Events]
where (convert(datetime,[Date],103) between '01-May-2015' and '15-May-2015') and [property] is not NULL
order by convert(datetime,[Date],103) desc
I believe this is happening because c.Date is a string field from the entity database. I have tried converting the date values to string and datetime to get the following to work, but I keep getting an operand error.
LINQ Query:
DateTime dat = DateTime.Now.AddDays(-10);
string preWeek = dat.ToString("dd-MMM-yyyy");
DateTime dtt = DateTime.Now;
string today = dat.ToString("dd-MMM-yyyy");
var data = db.Database_CRE_Events.Where(c => c.Date <= Convert.ToDateTime(preWeek) && c.property != null)
.Select(x => new Loan() { Name = x.Deal, loan = x.property, evnt = x.Event })
.ToList().Take(3);
return data;
Is it possible to convert the original sql query to a linq query as c.Date being a string parameter?
Thank you for any further assistance.
The problem is that you're introducing strings into the mix for no reason at all. Unless you have to convert a DateTime to or from a string, don't do it.
Your query should be as simple as:
DateTime preWeek = DateTime.Today.AddDays(-10);
var data = db.Database_CRE_Events
.Where(c => c.Date <= preWeek && c.property != null)
.Select(x => new Loan() { Name = x.Deal, loan = x.property, evnt = x.Event })
.ToList()
.Take(3);
return data;
If c.Date is actually a string, you should fix your database so that it isn't a string. It's meant to be a date, so represent it as a date! If you absolutely have to keep it as a string, you should at least use a sortable format, such as yyyy-MM-dd. At that point you could use CompareTo - but it's horrible :(
If the format is dd-MMM-yyyy (as it sounds) you could try performing the parse in the LINQ query, still passing in a DateTime but parsing each value in the database:
.Where(c =>
DateTime.ParseExact(c.Date, "dd-MMM-yyyy", CultureInfo.InvariantCulture) <= preWeek
&& c.property != null)
... but I wouldn't be surprised if that fails. You may want to add a view in SQL which gives a more appropriate version of the data. Fundamentally, if you have to work with a broken schema (in this case using the wrong type and making a poor decision about how to format the data within that type) then you should expect pain. Pass this pain up to managers in order to prioritize changing the schema...
Notes:
You're fetching all the data, and then just taking the first three elements. That's a bad idea. Switch round the calls to ToList and Take after addressing the next bullet...
"First three elements" is only meaningful with ordering. Use OrderBy to specify an ordering
You're not currently using today, so I removed it
If you're only interested in a date, use DateTime.Today
You should carefully consider time zones, both in your database and in your calling code. This is currently using the system default time zone - is that what you want?

List Collection Filtering using Where condition giving null values

I am getting the Values from DataBase View Table using the EDM
Query as IList Type.
it is giving some Collection of Elements.
From this collection, i am tring
to filter the data based on One Column but is doesn't giving the
filtered data even though the data is present based on condition The
query is as like below.
For Getting Data form DataBase // it is
fetching some collection of data.
IList<EFModel.EntityModel.vwGetActiveEmployee> activeEmployeelist = TimeOffService.GetActiveEmployees();
Here i want to fileter the data based on Column IsManger(values 1 or 0)
For that i wrote like
IList<EFModel.EntityModel.vwGetActiveEmployee> managerlist = activeEmployeelist.Where(p => p.IsManager == 1).Select(p => p) as IList<EFModel.EntityModel.vwGetActiveEmployee>;
But here the Managerlist showing null values. When i filter the data
using below
var emplistVar = activeEmployeelist.Where(p => p.IsManager.Equals(1)).Select(p => p);
it showing the some collection of data with "var" type but if i give
Class type it showing null. What is the reason here, This data is
taken from database View Data.
This code (reformatted to avoid scrolling):
IList<EFModel.EntityModel.vwGetActiveEmployee> managerlist
= activeEmployeelist.Where(p => p.IsManager == 1)
.Select(p => p)
as IList<EFModel.EntityModel.vwGetActiveEmployee>;
... will always give managerlist as a null, as Select doesn't return an IList<T> in any implementation I've ever seen. I suspect you want:
IList<vwGetActiveEmployee> managerlist =
activeEmployeelist.Where(p => p.IsManager == 1)
.ToList();
Note that unless you're deliberately performing a reference type conversion which can validly fail (in which case you should usually be checking the result against null) you should prefer casts instead of as. If you'd used a cast here, you'd have seen an exception immediately, because the value returned by Select wasn't of the type you were expecting.

Categories

Resources