getting a formatted nullable date string in a LINQ query - c#

This really seems like it should be working:
var result = (from x in Db.RebateDetail
where batch != null && batch != "" ? x.BatchNumber.Value == Convert.ToInt32(batch) : x.DatetimeCreated.Date >= Convert.ToDateTime(fromDate).Date && x.DatetimeCreated.Date <= Convert.ToDateTime(toDate).Date
select new
{
id = x.Id,
batchNumber = x.BatchNumber,
firstName = x.FirstName,
checkDate = x.CheckDate.Value.ToString("MM/dd/yy") ?? "",
checkNumber = x.CheckNumber
}).ToList();
The checkDate errors out with 'Nullable object must have a value.'
Is that not what the ?? should be doing?
I have tried several variations on this but cannot seem to make it happy.
Update: This is using Entity Framework and LinqToEF

Currently, "if CheckDate is null" it would yield a System.InvalidOperationException rather than take the value on the RHS of the ?? operator hence the error "Nullable object must have a value".
You'll need to ensure that either the LHS returns a value or the RHS does.
What you're looking for is the conditional operator ?:
checkDate = x.CheckDate.HasValue ?
x.CheckDate.Value.ToString("MM/dd/yy") : string.Empty

Related

Check for null before referencing joining table fails

I have a User table, which may have a related (FK) record in Address.
So in LINQ, I am trying to:
var data = _context.User.Where(x=>x.Deleted.HasValue == false)
.Select(y=> new MyObject {
Id = y.Id,
Name = y.Name,
Address = y.Address != null ? y.Address.Description : null
});
But as soon as I add the ternary operator (As I cannot reference y.Address.Description if there is no record there), my Select fails with a design-time error: Ambiguous Invocation.
What's the correct way to do this? In this case, there may be no 'Address' record.
Design time error on the 'select':
I think you need to cast the null to a string:
var data = _context.User.Where(x=>x.Deleted.HasValue == false)
.Select(y=> new MyObject {
Id = y.Id,
Name = y.Name,
Address = y.Address != null ? y.Address.Description : (string) null
});
I assume here that Description is of type string.
You could also try the Null-conditional operator:
var data = _context.User.Where(x=>x.Deleted.HasValue == false)
.Select(y=> new MyObject {
Id = y.Id,
Name = y.Name,
Address = y.Address?.Description
});
The problem has to do with type inference. The compiler cannot determine the result type of the ternary operator, thus it is necessary to explicitly indicate the type of the third operand (the null) by casting it.
See related question: Understanding C# compilation error with ternary operator
Simple example to cause this error:
int? i = true ? 1 : null;
On Visual Studio 2019 / .Net Framework 4.7.2, I get the following error:
Type of conditional expression cannot be determined because there is no implicit conversion between 'int' and '<null>'
Correction:
int? i = true ? 1 : (int?) null;

Multiple inline comparison while assigning values to object properties

I am working on c# data model class, I created instance and assigning values from another object. I am aware of single line comparision but I need multiple, I have tried && operator but compiler complain 'invalid opperator'. I am sure what I am missing, surly it must be simple!
var answerDataModel = new AnswerDataModel()
{
Id = answerId,
//need help to
Value = AnswerDtoObject.Answers.FirstOrDefault() == null? &&
isSubQuestionExist == true ? "_SBQA"
: AnswerDtoObject.Answers.FirstOrDefault(),
};
if I remove "&& isSubQuestionExist == true ?", then code works
You have an errant ? operator. Just combine your comparisons in a single conditional operator:
Value = (AnswerDtoObject.Answers.FirstOrDefault() == null && isSubQuestionExist == true) ?
"_SBQA" :
AnswerDtoObject.Answers.FirstOrDefault()
Value = (AnswerDtoObject.Answers.FirstOrDefault() == null) &&
(isSubQuestionExist == true) ? "_SBQA"
: AnswerDtoObject.Answers.FirstOrDefault(),

Linq Query using navigation properties and Where clause

I am trying to compose a linq query using navigation properties. I am selecting properties from 3 entities:
Lockers
SectionColumn
Contracts
I require ALL rows from the Lockers table where all the following conditions are met: the LockerTypeId = "308", .OutOfOrder != true, x.SectionColumn.SectionId == "52").
The query below without the condition x.SectionColumn.SectionId == "52" works and returns exactly what I require except rows with Section id of any value are returned as I would expect.
from l in Lockers.Where(x => x.LockerTypeId == "308" && x.OutOfOrder !=
true).DefaultIfEmpty()
select new
{
ColumnNumber = l.ColumnNumber,
LockerTypeId = l.LockerTypeId,
OutOfOrder = l.OutOfOrder,
Rented = l.Contracts.Select(x => x.Contract_ID < 0 ?
false : true).FirstOrDefault(),
Section = l.SectionColumn.SectionId
}
When I add the condition 'x.SectionColumn.SectionId == "52"' as below I get the error "The cast to value type 'System.Int32' failed because the materialized value is null". Either the result type's generic parameter or the query must use a nullable type" in linqpad. SectionId is a string (varchar in SQL Server).
from l in Lockers.Where(x => x.LockerTypeId == "308" && x.OutOfOrder !=
true).DefaultIfEmpty()
I would be grateful for assistance in correctly writing this query.
First off, your code might be a little more straight forward if you stick to pure LINQ. In that case, your code should look something like the following.
var results = from l in Lockers
where l.LockerTypeId == "308" && l.OutOfOrder != true && l.SectionColumn.SectionId == "52"
select new
{
ColumnNumber = l.ColumnNumber,
LockerTypeId = l.LockerTypeId,
OutOfOrder = l.OutOfOrder,
Rented = l.Contracts.Select(x => x.Contract_ID < 0 ? false : true).FirstOrDefault(),
Section = l.SectionColumn.SectionId
}
If l.SectionColumn.SectionId represents valid navigational properties and is of type string, then this should work correctly.
You really haven't done a thorough job of describing the issue (and it looks like you didn't stick around to field questions), but if l.SectionColumn is nullable, you should be able to update your code to something like this.
var results = from l in Lockers
let sectionId = (l.SectionColumn != null) ? l.SectionColumn.SectionId : null
where l.LockerTypeId == "308" && l.OutOfOrder != true && sectionId == "52"
select new
{
ColumnNumber = l.ColumnNumber,
LockerTypeId = l.LockerTypeId,
OutOfOrder = l.OutOfOrder,
Rented = l.Contracts.Select(x => x.Contract_ID < 0 ? false : true).FirstOrDefault(),
Section = l.SectionColumn.SectionId
}

LINQ and nullable parameter

I have a nullable field in DB and nullable parameter in model. I try the following LINQ to Entities query:
EditPersonViewModel model = (from i in db.Person
where i.PersonID == id.Value
select new EditPersonViewModel()
{
PersonID = i.PersonID,
Fullname = i.Fullname,
Comment = i.Comment,
Username = (i.UserId != null) ? i.AspNetUsers.UserName : String.Empty,
// this is parameter has type "int?"
PersonStatusID = (i.PersonStatus!=null) ? i.PersonStatus.PersonStatusID : null
}).FirstOrDefault();
I get the compilation error:
Error 1 Type of conditional expression cannot be determined because
there is no implicit conversion between 'int' and ''
it works fine with
Username = (i.UserId != null) ? i.AspNetUsers.UserName : String.Empty,
but does not work with "int?" type. Why and how to do it correctly?
Documentation of Conditional Operator says:-
Either the type of first_expression and second_expression must be the
same, or an implicit conversion must exist from one type to the other.
Since i.AspNetUsers.UserName & String.Empty are string types its working fine for you. Now, your problem is self explanatory because null cannot be casted to integer type, you need this instead:-
PersonStatusID = (i.PersonStatus!=null) ? i.PersonStatus.PersonStatusID : 0;
Or the other way around if you need that as Nullable of integer:-
PersonStatusID = (i.PersonStatus!=null) ? (int?)i.PersonStatus.PersonStatusID : null;
PersonStatusID should be of type int? in this case.

Null coalescing operator vs. value()

I'm creating some objects to return to a form via API, and the objects are derived from database values, including values that could be null in the database, but cannot be null in the context of my API (I am obtaining data from multiple tables, so I know that if a field is null in one table, I can obtain a legitimate value from another table):
List<ResultsByLineShiftSlot> returnResults = new List<ResultsByLineShiftSlot>();
foreach (LineShiftSlot ls in db.LineShiftSlots.OrderBy(ls => ls.ScheduledDateAndTime).Where(ls => ls.ProductionDate == slotDate &&
ls.ShiftId == shiftId &&
ls.LineId == lineId &&
ls.Quantity > 0 &&
ls.BlendId != null))
{
var recordedResult = db.LineShiftSlotResults.FirstOrDefault(r => r.LineShiftSlotId == ls.Id);
if (recordedResult != null)
{
ResultsByLineShiftSlot returnResult = new ResultsByLineShiftSlot
{
BlendId = recordedResult.BlendId
};
}
else
{
ResultsByLineShiftSlot returnResult = new ResultsByLineShiftSlot
{
BlendId = ls.BlendId ?? 0
};
}
}
return returnResults;
In the above example, BlendId can be null in LineShiftSlots, but not in LineShiftSlotResults.
In this context where a nullable variable is known to contain a non-null value, which is better?
Should I use the null coalescing operator:
BlendId = ls.BlendId ?? 0
Or should I use .Value():
BlendId = ls.BlendId.value()
Both compile, and seem to work.
Are they functionally equivalent in this context? Is using one over the other better practice? I know that .value() could potentially return an exception, whereas the null coalescing operator could not, but in this case .value can never be null.
In the general case, they're not semantically equivalent.
labelId = recordValue.labelId.Value;
This one means I know the value can't be null. Just throw if my assumption is wrong.
On the other hand,
labelId = recordValue.labelId ?? 0;
labelId = recordValue.labelId.GetValueOrDefault();
These two mean that the value may be null, and if that happens, just ignore it, considering it's normal, and substitute it with the default value.
I'd use GetValueOrDefault() in that case, it makes it somewhat more obvious (and the JIT will inline this method).
However, your code is equivalent to this simpler alternative:
labelId = recordValue.labelId ?? otherValue;
In this case the code would be the same however have you considered the following
labelId = recordValue.labelId ?? otherValue
which is essentially the following
if(recordValue.labelId != null){
labelId = recordValue.labelId.Value;
}
else
{
labelId = otherValue;
}

Categories