Better Way to Check for Null Value in Linq Path - c#

Here is an example of an assignment to a linq path pulled from code first...
applicants = appRegistrations
.ToList()
.Select(c => new ApplicantList() {
PartnerType = c.Participant != null ? c.Participant.PartnerType != null ? c.Participant.PartnerType.PartnerTypeName : "" : ""
});
Notice the null checks - is there a more elegant way I can write this code considering Participant AND PartnerType could be null?
I just hate checking for nulls on each property.

You could check if one of both are null:
List<ApplicantList> applicants = appRegistrations
.Select(ar => c.Participant == null || c.Participant.PartnerType == null
? "" : c.Participant.PartnerType.PartnerTypeName)
.Select(str => new ApplicantList { PartnerType = str })
.ToList();

You can shorten it a little bit:
PartnerType = c.Participant != null && c.Participant.PartnerType != null
? c.Participant.PartnerType.PartnerTypeName
: ""

When you construct the objects such as Participant, make sure that the properties are never null. Think if null is a valid value for a Participant? If not then you should never allow it to be null. Take a constructor parameter and add a guard clause to check for nulls. Otherwise initialize them to a default value.
Also, see NULL Reference Pattern.

Related

c# Linq Chain with avoid null exeption

i have multi tables with one to many relation like chain
1- address has postal code id
2- postal table has area id
3- area table has city id
4- city table has county id
5- county table has country id
6- and at the last country table
from every table i need to get street name , full postal code, area name , city name, county name , country name
the query is as follow
var address = from add in _Database.Addresses
select add;
address.Select(x=>new AddressClass {
BuildingNameOrNumber=x.BuildingNameOrNumber,
MainStreet = x.Postcode ==null ? string.Empty: x.Postcode.StreetName,
FullPostCode = x.Postcode == null ? string.Empty :x.Postcode.FullPostcode,
AreaName = x.Postcode == null ? string.Empty : x.Postcode.Area == null ? string.Empty: x.Postcode.Area.Name,
CityName = x.Postcode == null ? string.Empty : x.Postcode.Area == null ? string.Empty : x.Postcode.Area.City == null ? string.Empty: x.Postcode.Area.City.Name,
CountyName = x.Postcode == null ? string.Empty : x.Postcode.Area == null ? string.Empty : x.Postcode.Area.City == null ? string.Empty : x.Postcode.Area.City.County == null ?string.Empty: x.Postcode.Area.City.County.Name,
CountryName= x.Postcode == null ? string.Empty : x.Postcode.Area == null ? string.Empty : x.Postcode.Area.City == null ? string.Empty : x.Postcode.Area.City.County == null ? string.Empty : x.Postcode.Area.City.County.Country == null ? string.Empty:x.Postcode.Area.City.County.Country.CountryName
})
i need to replace this multi conditions with one condition for every property
This is too complex for me to wrap my head around sorry :)
But what you need is the ? and ?? operators. Second to last item in your really complex query will be like:
CountyName = x.Postcode?.Area?.City?.County?.Name ?? string.Empty
x?.y returns y if x is not null and returns null otherwise. If y is not nullable then result of x?.y will become Nullable<T> where T is typeof(y)
x ?? y returns x if x is not null and returns y if it is. Type of x and y should be the same here.
Hope it helps
Edit
In the second look I see that you are using LINQ with IQueryable which can't use the null propagating operator. So in this case you don't have this option you can either load all the data using a ToList() run your query on them if the data size is small. Or you can load each part in separate queries (which due to the number of sub element I don't recommend). Alternatively you can use tools like this
My advice is the first option however may not be memory friendly on large datasets.
Sorry for the initial hasty answer. 🙏

Null value in linq where clause

I'm having an issue where I want to return results where something matches and I get an error if one of the properties I'm trying to match is null.
if (!string.IsNullOrEmpty(searchString))
{
Infos = Infos.Where(
x =>
x.FirstName.ToLower().Contains(searchString) ||
x.LastName.ToLower().Contains(searchString) ||
x.ContractNum.ToLower().Contains(searchString) ||
x.VIN.ToLower().Contains(searchString) ||
x.Claim.InitiatedBy.ToLower().Contains(searchString)
).ToList();
}
If ContractNum or VIN, for example, are null then it throws an error. I'm not sure how to check if one of these are null inside of a linq query.
You can add explicit null checks:
Infos = Infos.Where(
x =>
(x.FirstName != null && x.FirstName.ToLower().Contains(searchString)) ||
(x.LastName != null && x.LastName.ToLower().Contains(searchString)) ||
(x.ContractNum != null && x.ContractNum.ToLower().Contains(searchString)) ||
(x.VIN != null && x.VIN.ToLower().Contains(searchString)) ||
(x.Claim != null && x.Claim.InitiatedBy != null && x.Claim.InitiatedBy.ToLower().Contains(searchString))
).ToList();
You have multiple options, first is to do an explicit check against null and the other option is to use Null propagation operator.
x.FirstName != null && x.FirstName.ToLower().Contains(searchString)
or
x.FirstName?.ToLower()?.Contains(searchString) == true
But I would suggest you to use IndexOf instead of Contains for case
insensitive comparison.
something like:
x.FirstName?.IndexOf(searchString, StringComparison.CurrentCultureIgnoreCase) >= 0)
Checking the property is null or empty before comparing it it's the only way I know
if (!string.IsNullOrEmpty(searchString))
{
Infos = Infos.Where(
x =>
(!String.IsNullOrEmpty(x.FirstName) && x.FirstName.ToLowerInvariant().Contains(searchString)) ||
(!String.IsNullOrEmpty(x.LastName) && x.LastName.ToLowerInvariant().Contains(searchString)) ||
(!String.IsNullOrEmpty(x.ContractNum) && x.ContractNum.ToLowerInvariant().Contains(searchString)) ||
(!String.IsNullOrEmpty(x.VIN) && x.VIN.ToLowerInvariant().Contains(searchString)) ||
(x.Claim != null && !String.IsNullOrEmpty(x.Claim.InitiatedBy) && x.Claim.InitiatedBy.ToLowerInvariant().Contains(searchString))
).ToList();
}
EXTRA: I added a check on the Claim property to make sure it's not null when looking at InitiatedBy
EXTRA 2: Using the build in function IsNullOrEmpty to compare string to "" and nullso the code is clearer.
Extra 3: Used of ToLowerInvariant (https://msdn.microsoft.com/en-us/library/system.string.tolowerinvariant(v=vs.110).aspx) so the lowering action will act the same no matter of the culture.
You could use ?? to replace it with a acceptable value.
(x.ContractNum??"").ToLower()
I would use the null conditional operator ?, this will however, return a nullable bool? so you will need to handle that appropriately.
Some examples on how to do this:
x?.FirstName?.ToLower().Contains(searchString) == true;
x?.FirstName?.ToLower().Contains(searchString) ?? false;
An alternative method to keep the comparison logic in one place to use a sub collection of the properties and check on those:
Infos = Infos.Where(i=>
new[] {i.FirstName,i.LastName,i.ContractNum /*etc*/}
.Any(w=> w?.ToLower().Contains(searchString) ?? false))
.ToList();
(It does read out all properties, but that shouldn't cost much performance and gains much maintainability )

Check system.nullreferenceexception

My code as follows:
#{var UName = ((IEnumerable<Pollidut.ViewModels.ComboItem>)ViewBag.UnionList).FirstOrDefault(x => x.ID == item.UNION_NAME_ID).Name;<text>#UName</text>
if ViewBag.UnionList is empty then it troughs system.nullreferenceexception.How to check and validate this?
Well, you're calling FirstOrDefault - that returns null (or rather, the default value for the element type) if the sequence is empty. So you can detect that with a separate statement:
#{var sequence = (IEnumerable<Pollidut.ViewModels.ComboItem>)ViewBag.UnionList;
var first = sequence.FirstOrDefault(x => x.ID == item.UNION_NAME_ID);
var name = first == null ? "Some default name" : first.Name; }
<text>#UName</text>
In C# 6 it's easier using the null conditional operator, e.g.
var name = first?.Name ?? "Some default name";
(There's a slight difference here - if Name returns null, in the latter code you'd end up with the default name; in the former code you wouldn't.)
First of all, you should not be doing this kind of work in the View. It belongs in the Controller. So the cshtml should simply be:
<text>#ViewBag.UName</text>
And in the controller, use something like:
var tempUnion = UnionList.FirstOrDefault(x => x.ID == item.UNION_NAME_ID);
ViewBag.UName = tempUnion == null ? "" : tempUnion.Name;

Inline If statement - short-circuiting

As I understand and read you can use short circuiting in if statement (&& or ||) in order for second condition not to fire. and if you want both condition to fire you would use single operands (& or |).
So say if I have inline if statement as below :
var test = (MyObject != null || string.IsNullOrEmpty(MyObject.Property)) ? string.Empty : MyObject.Property;
This will throw object reference error if MyObject is null, which in my opinion should not as I am using short circuiting. Can someone please explain this.
You're using the wrong condition. This part:
MyObject != null || string.IsNullOrEmpty(MyObject.Property)
should be:
MyObject == null || string.IsNullOrEmpty(MyObject.Property)
The RHS of an || only executes if the left hand is false. You want it to only execute if MyObject is not null.
EDIT: If you really want the MyObject != null part, you could change the whole thing to:
var test = MyObject != null && !string.IsNullOrEmpty(MyObject.Property)
? MyObject.Property : "";
Note the reversal of the 2nd and 3rd operands of the conditional operator too though.
You should have an == not an !=
var test = (MyObject == null || string.IsNullOrEmpty(MyObject.Property) ? string.Empty : MyObject.Property
Try this:
var test = (MyObject == null || string.IsNullOrEmpty(MyObject.Property)
? string.Empty : MyObject.Property
MyObject != null || string.IsNullOrEmpty(MyObject.Property)
Here you say.
If my object is not null.
or
string.IsNullOrEmpty(MyObject.Property)
Which means that if MyObject is null he will try to execute the second part.
MyObject == null || string.IsNullOrEmpty(MyObject.Property)
This won't throw null exception
That happens because MyObject is null and therefore the first condition is false so the second part must be evaluated to know the whole of the condition. Change the line to this:
MyObject != null && string.IsNullOrEmpty(MyObject.Property)
You should prefer readability instead of line-count, e.g.:
string prop = string.Empty;
if(MyObject != null && MyObject.Property != null)
prop = MyObject.Property;
(the reason for your exception was already explained in other answers)

Left outer join , Object reference not set to an instance of an object

I am trying to perform left outer join on 2 objects and getting an error : Object reference not set to an instance of an object.
The objects look like that
var deliverables = OCHART.GetACAPValues(organization, ReportingPeriod, FiscalYear, "(09-10.10a) Outreach Significant").ToList();
var references = (from rf in OCHART.References where rf.RefType.Equals("09-10.10a") && rf.Comments.Equals("2") select rf).ToList();
In which deliverables might often return 0 records. Unfortunately I cannot just go and join two tables from database so deliverables must be an object.
Can somebody please point me in the right direction
Thanks,
My code is
var items = (from rf in references
join pt in deliverables on rf.Description equals pt.b into prt
from x in prt.Where(prt2 => prt2.a.Equals(audience)).DefaultIfEmpty()
where rf.RefType.Equals("09-10.10a") && rf.Comments.Equals("2")
select new
{
audience = (string)(audience == null ? "" : audience),
RefType = (string)(rf.RefType == null ? "" : rf.RefType),
RefOrder = (int)(rf.RefOrder == null ? 0 : rf.RefOrder),
refName = (string)(rf.Description == null ? "" : rf.Description),
collumn_attr = (string)(x.b == null ? string.Empty : x.b),
value = (int)(x.ACAP == null ? (int?)null : x.ACAP)
})
.OrderBy(o => o.RefOrder)
.Take(9)
.ToList();
EDIT:
After some more debuging it appears that I get error on following lines in my code
collumn_attr = (string)(x.b == null ? string.Empty : x.b),
value = (int)(x.ACAP == null ? (int?)null : x.ACAP)
I noticed even when I have values (added for testing) in deliverables and when values are matching the query will execute properly, but when there is no match in deliverable that's when I get the error message.
The issue is probably with handling the null values.
I think x is null and is causing a NullReferenceException in the following lines:
collumn_attr = (string)(x.b == null ? string.Empty : x.b),
value = (int)(x.ACAP == null ? (int?)null : x.ACAP)
This judgment is based on the from x in line's DefaultIfEmpty() call, typical of left-outer-joins.
In database code, you would write something like x.ACAP == null to detect the case where there was no matching join element. If you change this replace the 'x.property == null' checks with "x == null" checks, I suspect your problem will clear up.
There's still the problem with the second line - you're going to get an exception at run-time if you try to cast the value (int?)null to an integer. Using a meaningful default int value such as 0 in the case that x == null will clear that up.
If you step through your code, before the query is executed do you actually see "deliverables" and "references" being populated with data ?
Investigate all child tables/properties you're using in your query. The reason you're getting that error is most likely because one of the properties you're using while comparing is null.
.RefType .Comment for example.
Maybe .RefType is null and it's having problems sorting at the end. Difficult to say without seeing what's in those two collections.
Added after your comment:
Note that it's better not to use .Equals() when your variable could be null. Use == instead. Reference: http://www.dotnetperls.com/string-equals
Also imagine that (x.ACAP == null ? (int?)null : x.ACAP) returns a null.
You're casting that whole thing as an int : value = (int)(x.ACAP == null ? (int?)null : x.ACAP). Casting null as n int will obviously fail
Just for the record, the new Null-conditional operators in C# 6.0 could be used like this:
collumn_attr = x?.b ?? string.Empty,
value = x?.ACAP

Categories

Resources