LINQ and nullable parameter - c#

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.

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;

Converting null literal or possible null value to non-nullable type

Is it possible to resolve this warning:
Converting null literal or possible null value to non-nullable type.
without suppression for this C# code
List<PropertyInfo> sourceProperties = sourceObject.GetType().GetProperties().ToList<PropertyInfo>();
List<PropertyInfo> destinationProperties = destinationObject.GetType().GetProperties().ToList<PropertyInfo>();
foreach (PropertyInfo sourceProperty in sourceProperties)
{
if (!Equals(destinationProperties, null))
{
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
PropertyInfo destinationProperty = destinationProperties.Find(item => item.Name == sourceProperty.Name);
#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type.
}
}
that uses Reflection.
I am using Visual Studio 2019 and .NET Core 3.1.
Find() can return null when what you're looking for is not found. So destinationProperty can become null.
So the solution would be to declare it as nullable:
PropertyInfo? destinationProperty = ...
Or to throw an exception:
PropertyInfo destinationProperty = ...Find() ?? throw new ArgumentException(...)
You can take one of the solutions proposed by #codecaster or, as Microsoft docs describe, you can alternatively disable this warning by adding the null forgiving operator, ! to the right-hand side
PropertyInfo destinationProperty = destinationProperties.Find(item => item.Name == sourceProperty.Name)!; //<------ note the "!" operator at the end of the line
You can do one thing, instead of using the type 'PropertyInfo'
PropertyInfo destinationProperty = destinationProperties.Find(item => item.Name == sourceProperty.Name);
You can use 'var'
var destinationProperty = destinationProperties.Find(item => item.Name == sourceProperty.Name);

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

Error in MVC Linq

I have Linq query in MVC
obj_CCM.TeamName = (from c in db.Team_Master
where c.Id != TeamId
select new TypeTeam
{
TeamID = c.Id,
TeamName = c.TeamName
}
).ToList();
Session["TeamId"] = obj_CCM.TeamName.Count > 0 ? Convert.ToInt32(obj_CCM.TeamName[0].TeamID) : -1;
I facing below error :
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 guess issue is at this line:
TeamID = c.Id
In your Team_Master table, the Id column must be Nullable so it is returning null but property TeamID in TypeTeam is defined as int and can't hold null values. So you need to either disallow your Id column in database to accept null values or make the TeamID property as nullable of integer.
public int? TeamID{ get; set; }
Also, as a side note, your query is returning List<TypeTeam> but you are storing that in obj_CCM.TeamName, either this is a typo or you may have defined TeamName of type List<TypeTeam>.
Check first, if Session["TeamId"] != null and then do the converting

Linq to Sql Enums and Conditional Operator

Whichever way I do it, it seems something goes wrong when using the conditional operator on enum values in Linq to Sql. For example
var ret = from listings in db.Listings
select new Listing
{
ID = listings.ID,
//etc
OrderStatus = listings.OrderItems.Count > 0
? listings.OrderItems.First().Order.OrderStatus : OrderStatus.NotCheckedOut
};
System.Data.SqlClient.SqlException:
Conversion failed when converting the
nvarchar value 'Charged' to data type
int..
When I leave the enum field as nvarchar mapped to a string I get similar conversion errors. How to workaround this ?
This works, maybe there's a better way though ?
var ret = from listings in db.Listings
let ordS = listings.OrderItems.Count > 0 ?
listings.OrderItems.First().Order.OrderStatus.ToString() : OrderStatus.NotCheckedOut.ToString()
select new Listing
{
ID = listings.ID,
//etc
OrderStatus = (OrderStatus)Enum.Parse(typeof(OrderStatus), ordS)
};

Categories