Check for null before referencing joining table fails - c#

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;

Related

getting a formatted nullable date string in a LINQ query

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

LINQ: Set a default value if navigation property is null

This is my current LINQ statement:
var results = from l in leads
select new MyObject
{
LeadID = l.LeadID,
SelectedProposalEngineerID = l.LeadContacts.Where(contact => contact.LeadContactTypeID == LeadContactType.ProposalEngineer).FirstOrDefault().ContactID
};
The trouble I'm having is that the last item is often null. So when I try to convert "results" to a List, I get
{"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."}
I don't want to make SelectedProposalEngineerID a nullable int, for problems that would cause down stream. How would I give it a value of 0 when it's null?
I have seen a LOT of other threads about this, but I can't seem to adapt any of their answers to this case.
Use DefaultIfEmpty extension method.
var results = from l in leads
select new MyObject
{
LeadID = l.LeadID,
SelectedProposalEngineerID =
l.LeadContacts.Where(contact => contact.LeadContactTypeID == LeadContactType.ProposalEngineer)
.Select(contact => contact.ContactID)
.DefaultIfEmpty(0)
.FirstOrDefault()
};
Nullable<int> ID;
var results = from l in leads
select new MyObject
{
LeadID = l.LeadID,
SelectedProposalEngineerID = (ID = l.LeadContacts.Where(contact => contact.LeadContactTypeID == LeadContactType.ProposalEngineer).FirstOrDefault().ContactID).HasValue ? ID.Value : 0;
};
A ternary operator should do the job. Assign the result to a variable, then if it's not null, cast the variable to int and return it, else return 0.

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.

The property 'x' on 'tblX' could not be set to a 'null' value. You must set this property to a non-null value of type 'Int16'

List<tblX> messages = (from x in db.tblX where x.msg_id == id_id
|| x.name == firstName select x).ToList();
I get the error:
The property 'x' on 'tblX' could not be set to a 'null' value. You must set this property to a non-null value of type 'Int16'.
I have a property msg_blocked in db, which is nullable and integer. I know that I need to make a conversion, but I don't use it or need it anywhere in my linq.
Seems like your class definition for tblX doesn't match the database representation, so either modify your class to accept a nullable value, or just project out the required fields:
List<tblX> messages = (from x in db.tblX
where (x.msg_id == id_id || x.name == firstName)
select new tblX
{
//required fields
msg_id = x.msg_id,
name = x.name,
...
}).ToList();
Addendum: The reason you run into this problem is behind the scenes when you
select x
this is translated into a
select new tblX
which projects into all its available fields. The code provided is more explicit and specifies which fields to query for and then project into.
List<tblX> messages = (from x in db.tblX
where (x.msg_id == id_id || x.name == firstName) &&
x.msg_blocked != null
select x).ToList();

Problem with LINQ to Entities query using Sum on child object property

Given this query:
from s in services
select new
{
s.Id,
s.DateTime,
Class = s.Class.Name,
s.Location,
s.Price,
HeadCount = s.Reservations.Sum(r => r.PartySize), // problem here. r.PartySize is int
s.MaxSeats
}
If the service doesn't have any reservations, this exception is thrown:
System.InvalidOperationException: The cast to value type 'Int32' failed because the materialized value is null. Either the result type's generic parameter or the query must use a nullable type.
I get it, but how should I deal with it? My intention is if there are no reservations, then HeadCount be assigned 0.
There's an even simpler solution:
from s in services
select new
{
s.Id,
s.DateTime,
Class = s.Class.Name,
s.Location,
s.Price,
HeadCount = (int?)s.Reservations.Sum(r => r.PartySize),
s.MaxSeats
}
Note the cast. This may also produce simpler SQL than #Ahmad's suggestion.
Essentially, you're just helping out type inference.
You should check for it:
HeadCount = s.Reservations != null ? s.Reservations.Sum(r => r.PartySize) : 0,
This should resolve your problem:
Try to cost the int to int?
from s in services
select new
{
s.Id,
s.DateTime,
Class = s.Class.Name,
s.Location,
s.Price,
HeadCount = s.Reservations.Sum(r => (int?) r.PartySize),
s.MaxSeats
};
HeadCount = HeadCount ?? 0;
A simple ternary operator should fix the problem nicely...
something like this:
HeadCount = (s.Reservations != null && s.Reservations.Any()) ? s.Reservations.Sum(r => r.PartySize) : 0;
This will handle for both null and empty situations

Categories