I have this LINQ statement which returns null if the sequence is empty. I assign the result into a nullable DateTime. Later on I check if the DateTime.HasValue property and ReSharper tells me the expression is always true.
DateTime? latestUploadDateInBLSO = Documents.Where(d => d.DocumentLinkId == documentLinkId &&
d.UploadedInStage.StageNumber == 6 &&
d.DocumentOwnerTeam.TeamId == AuthorUser.Team.TeamId)
.Select(d => d.UploadedOnDate)
.DefaultIfEmpty()
.Max();
if (latestUploadDateInBLSO.HasValue) { // <-- Says this is always true
Documents.Single(d => d.DocumentLinkId == documentLinkId &&
d.UploadedOnDate == latestUploadDateInBLSO &&
d.UploadedInStage.StageNumber == 6 &&
d.DocumentOwnerTeam.TeamId == AuthorUser.Team.TeamId).IsLatestVersion = true;
}
Since latestUploadDateInBLSO can be null, how is that expression always true?
As UploadedOnDate isn't nullable, the result will always be a DateTime value and never null. If the list is empty, you'll get default(DateTime), which is DateTime.MinValue.
If you want it to return null, you'll need to cast your UploadedOnDate to DateTime?. You can omit the DefaultIfEmpty as per the docs Max will return null for an empty sequence if the type is nullable.
DateTime? latestUploadDateInBLSO = Documents
.Where(d => d.DocumentLinkId == documentLinkId && d.UploadedInStage.StageNumber == 6 && d.DocumentOwnerTeam.TeamId == AuthorUser.Team.TeamId)
.Select(d => (DateTime?)d.UploadedOnDate)
.Max();
If uploadedOnDate is also of DateTime type then it's not NULL. The default value for DateTime is equal to DateTime.MinValue. That is why your nullable will always have a value. If you want to change this you'll explicitly have to say via DefaultIfEmpty and return NULL as default value.
It's because of the DefaultIfEmpty call combined with a sequence of non-nullable elements (DateTime) - you're saying if the collection returned from the Where and Select is empty, to instead return a collection with a single defaulted DateTime within it, so it will never return null.
Here's a small sample with the output from LINQPad:
List<DateTime> l = new List<DateTime>();
DateTime x = l.DefaultIfEmpty().Max();
x.Dump();
var y = new DateTime();
y.Dump();
l.DefaultIfEmpty().Dump();
DefaultIfEmpty is probably initializing your DateTime object to its default value which is DateTime.MinValue so it's never null and thus HasValue will always return true.
Related
I have the following:
var tagId = "5288";
source.Where(p => p.GetPropertyValue<IEnumerable<string>>("picker").Contains(tagId));
This returns the error System.ArgumentNullException: Value cannot be null.
So some of the returned results, does not contain a picker value. How would I check for this, in the above statement?
It is a Umbraco Multinode treepicker that is the "picker" value.
If I understand correctly, the result of GetPropertyValue can be null if the picker value is not found. In that case, you can use a null conditional operator:
source.Where(p => p.GetPropertyValue<IEnumerable<string>>("picker")?.Contains(tagId) == true);
Note the ?. after GetPropertyValue. If that method returns null then it's not true, so those will not be included in the filtered objects.
Use this:
source.Where(p =>
{
var pickerVal = p.GetPropertyValue<IEnumerable<string>>("picker");
if (pickerVal == null)
return false;
return pickerVal.Contains(tagId);
});
or more condensed:
source.Where(p =>
{
var pickerVal = p.GetPropertyValue<IEnumerable<string>>("picker");
return (pickerVal != null) && pickerVal.Contains(tagId);
});
I want to retrieve an Id from a table based on certain criteria. If the criteria isn't met, I just want to return the default value (in this case, I'm assuming null). Here's my code:
int? circuitTypeId = cimsContext.CircuitTypes.FirstOrDefault(ct => ct.Name == circuitType).Id;
I am assuming that if there isn't a match between ct.Name and circuitType that the value returned is null. However, when running my program, I get an error on this line saying "Null reference exception". I know that the record isn't there, but shouldn't I be able to assign the null returned by the query to my nullable int variable?
Your assumption is wrong, as per your query:-
int? circuitTypeId = cimsContext.CircuitTypes
.FirstOrDefault(ct => ct.Name == circuitType).Id;
This will return int, when match is found but it will throw a Null Reference Exception when no name matches with that of circuitType. you should do this:-
var circuitTypeObj = cimsContext.CircuitTypes
.FirstOrDefault(ct => ct.Name == circuitType);
int? circuitTypeId = circuitTypeObj == null ? (int?)null : circuitTypeObj.Id;
Try this :
int? circuitTypeId = cimsContext.CircuitTypes.Where(ct => ct.Name == circuitType).Select(p => p.Id).FirstOrDefault();
In C# version 6 you can simply add a ? before id to prevent the properties of null objects from being referenced.
int? circuitTypeId = cimsContext.CircuitTypes.FirstOrDefault(ct => ct.Name == circuitType)?.Id;
You should first check for null before assigning the circuitTypeId variable:
int? circuitTypeId;
var circuitType = cimsContext.CircuitTypes.FirstOrDefault(ct => ct.Name == circuitType);
if(circuitType != null)
{
circuitTypeId = circuitType.Id;
}
The problem is .Id is probably trying to reference a null CirctuitType.
var circuitType = cimsContext.CircuitTypes.FirstOrDefault(ct => ct.Name == circuitType);
int? circuitTypeId = circuitType.Id; // if circuitType is null, a null reference exception is thrown
Instead, you'll need to test the reference for null:
var circuitType = cimsContext.CircuitTypes.FirstOrDefault(ct => ct.Name == circuitType);
int? circuitTypeId = (circuitType == null ? (int?)null : circuitType.Id);
I have a query like this :
result =
firstIdeaRepository.FindBy(
i => i.FirstIdeaState == FirstIdeaState && i.Date >= start && i.Date <= end)
.AsEnumerable()
.Select(j => new RptListOfCompanyBasedOnFirstIdeaState()
{
Name =
companyRepository.FindBy(i => i.UserId == j.UserId)
.FirstOrDefault()
DateOfMeeting =
calenderRepository.ConvertToPersianToShow(
meetingReposiotry.FindBy(s => s.FirstIdeaId == j.Id)
.FirstOrDefault()
.Date),
DateOfExit =
calenderRepository.ConvertToPersianToShow(j.DateOfExit.Value),
ReasonOfExit = j.ReasonOfExit,
}).ToList();
return result;
As you can see i use FirstOrDefault() and j.DateOfExit.Value and sometimes my Date doesn't have any values or sometime my other variables are null too because i use firstordefaut() like
companyRepository.FindBy(i => i.UserId == j.UserId).FirstOrDefault().
So my query throws a null exception and the result can't be created ,how can i handle this exception and for example if the .NET detects the null value ignores it by default or uses a default values for that ?
Best regards.
I would make the following changes:
result =
firstIdeaRepository.FindBy(
i => i.FirstIdeaState == FirstIdeaState && i.Date >= start && i.Date <= end)
.AsEnumerable()
.Select(j => new RptListOfCompanyBasedOnFirstIdeaState()
{
Name =
companyRepository.FindBy(i => i.UserId == j.UserId)
.FirstOrDefault()
DateOfMeeting =
callenderRepository.ConvertToPersianToShow(
meetingReposiotry.FindBy(s => s.FirstIdeaId == j.Id)
// project a new sequence first, before calling `FirstOrDefault`:
.Select(s => s.Date)
.FirstOrDefault(),
DateOfExit =
j.DateOfExit.HasValue ?
callenderRepository.ConvertToPersianToShow(j.DateOfExit.Value) :
null,
ReasonOfExit = j.ReasonOfExit,
}).ToList();
When you use FirstOrDefault, there's a possibility that you'll get null back (in the case of reference types), and so you need to plan for that in your code.
For example, when assigning DateOfMeeting, you could project the results (using .Select) before using .FirstOrDefault, so that you're not ever accessing the Date property on what could be a null value.
As for DateOfExit, I've used the conditional operator to determine whether to call the calendarRepository's method at all. This assumes that DateOfExit is nullable.
Unrelated: "Calendar" is spelled with one "l" and not two.
Since you're using a nullable date, you can try filtering by values that have date, something like:
.FindBy(s => s.FirstIdeaId == j.Id && s.Date.HasValue)
This will ensure that you don't get any records with null date.
As I mentioned in comments, other cases need to be handled on case-by-case basis. Judging by the code you've shown, maybe you can handle Name as:
Name = companyRepository.FindBy(i => i.UserId == j.UserId).FirstOrDefault() ?? "anonymous";
and so on.
Another example:
If you do want to get the record even if DateOfMeeting is null, then add a check for HasValue in subsequent part or default it to some date:
DateOfExit = j.DateOfExit.HasValue ?
callenderRepository.ConvertToPersianToShow(j.DateOfExit.Value)
: (DateTime)null, // you need to make `DateOfExit` nullable and then handle that downstream
// or (default with current date)
DateOfExit = j.DateOfExit.HasValue ?
callenderRepository.ConvertToPersianToShow(j.DateOfExit.Value)
: callenderRepository.ConvertToPersianToShow(DateTime.Now),
// or (default with empty date)
DateOfExit = j.DateOfExit.HasValue ?
callenderRepository.ConvertToPersianToShow(j.DateOfExit.Value)
: callenderRepository.ConvertToPersianToShow(new DateTime()),
Moral of the story: figure out what the default value should be in case of null and then substitute that accordingly in the query when calling FirstOrDefault().
The broadest solution would be to use the idea of an null object with the DefaultIfEmpty<T>(T DefaultValue) method in your query. An example would be:
var defaultMeeting = new Meeting() { Date = new DateTime() };
var dateOfMeeting = meetingRepository.FindBy(s => s.FirstIdeaId == j.Id)
.DefaultIfEmpty(defaultMeeting)
.FirstOrDefault()
.Date;
using (AdviserReserveEntities Adv=new AdviserReserveEntities())
{
decimal sum= Adv.Credits.Where(u => u.UserIdRef == 2).Sum(a => a.Amount);
}
if no record founds following exception is occurred:
The cast to value type 'Int64' failed because the materialized value
is null. Either the result type's generic parameter or the q`uery must
use a nullable type.
how to return 0 if no record founds?
One way is to use a nullable type:
decimal? sum = Adv.Credits
.Where(u => u.UserIdRef == 2)
.Sum(a => (decimal?)a.Amount);
Watch the cast in the sum expression. This way you will get null back when there was no element. You can add the coalesce operator to get a default value:
decimal sum = Adv.Credits
.Where(u => u.UserIdRef == 2)
.Sum(a => (decimal?)a.Amount)
?? 0;
The solution mentioned by Raphael and Justin is worse, because first checking with Any() will cause in two database queries instead of just one.
var records = Adv.Credits.Where(u => u.UserIdRef == 2);
decimal sum = records.Any() ? records.Sum(a => a.Amount) : 0m;
I am trying to return a Guid value below. However the database(and my dbml) has that column as a nullable Guid and it is generating an exception on the .Select portion saying it can't convert from an IQueryable<System.Guid?> to a System.Guid.
I am guessing I need to make my return value "concrete" first???? True?
If so how do I do that with Guid's?
public static Guid GetCurrentWorkerByType(int enrollmentID, int staffTypeID)
{
using (var context = CmoDataContext.Create())
{
IQueryable<tblWorkerHistory> tWorkHist = context.GetTable<tblWorkerHistory>();
return (tWorkHist.Where(workHist =>
(workHist.EnrollmentID == enrollmentID) &&
(workHist.tblStaff.StaffTypeID == staffTypeID) &&
(workHist.EndDate == null || workHist.EndDate > DateTime.Now))
.Select(workHist => workHist.Worker));
}
}
}
// Get the Guid? itself, for sake of example from IQueryable<Guid?>
// (could be `null` from DB value or because queryable was empty)
Guid? maybeGuid = queryable.FirstOrDefault();
// Need to have *a* GUID or default it to something
// because a Guid is a value-type and needs *a* value.
Guid theGuid = maybeGuid ?? Guid.Empty;
Also see Nullable<T>.HasValue/Value -- A longer, but equivalent, method would be:
Guid theGuid = maybeGuid.HasValue ? maybeGuid.Value : Guid.Empty;
Observe that HasValue may be suitable in general if statements to change logic and also note that Value will throw an exception if maybeGuid is "has no value" -- is null -- which is why the guard is required.
Happy coding.
Pedantic detail: The equivalent method is not "thread safe". That is, assuming maybeGuid was shared, it could be assigned null between HasValue and Value. There are a number of SO questions that cover the "thread safety" of ?? (the coalesce operator) -- the generated IL effectively uses a temporary variable so the value may be stale but an exception can't be thrown.
Use
.Select(workHist => workHist.Worker).Single();
.Select() returns a query that has not run.
If you use .Select().ToList() then you you return a list.
If you use .Select().Single() then you return one item and it makes sure only one item is there
If you use .Select().SingleOrDefault() then you return one item and default. Query must not contain more than 1 item.
If you use .Select().First() then you return the first item. Query must contain at least 1 item.
If you use .Select().FirstOrDefault() then you return the first item or default. Query can contain 1 or more or no items.
Try
Change your return type from System.Guid to ?System.Guid // nullable of guid
Then add .FirstOrDefault() after the select call
A struct cannot be null, but the System.Nullable class wraps the struct in a class.
What you've got is a query that hasn't executed for one, and for two, it will return a list of Guids as far as I can tell. If you want to return the first Guid or default (which I believe is a zero'd out guid) you can say .FirstorDefault() after your select.
The closest you will get representing null with a Guid is to use Guid.Empty. So, for your query, you will probably want to first pick the value returned by FirstOrDefault, make a null check, and then return a reasnable value:
Guid? result = tWorkHist.Where(workHist =>
(workHist.EnrollmentID == enrollmentID)
&& (workHist.tblStaff.StaffTypeID == staffTypeID)
&& (workHist.EndDate == null || workHist.EndDate > DateTime.Now))
.Select(workHist => workHist.Worker)
.FirstOrDefault();
return result.HasValue ? result.Value : Guid.Empty;
Use FirstOrDefault with conjunticon with Guid.Empty
Try this:
public static Guid GetCurrentWorkerByType(int enrollmentID, int staffTypeID)
{
using (var context = CmoDataContext.Create())
{
IQueryable<tblWorkerHistory> tWorkHist = context.GetTable<tblWorkerHistory>();
var guid = (tWorkHist.Where(workHist => (workHist.EnrollmentID == enrollmentID) &&
(workHist.tblStaff.StaffTypeID == staffTypeID) &&(workHist.EndDate == null || workHist.EndDate > DateTime.Now))
.Select(workHist => workHist.Worker)
///####NOTICE THE USE OF FirstOrDefault
).FirstOrDefault();
return (guid.HasValue)?guid.Value:Guid.Empty
}
}