null check nested objects before using SelectMany - c#

I have list of Countries and inside it have list of Places.
// ...
public IList<ICountriesDTO> Countries { get; set; }
public class CountriesDTO: ICountriesDTO
{
public IEnumerable<IPlacesDTO> Places { get; set;
}
I am trying to get list of Places that are not null.
allPlacesDTO.World.Countries
.SelectMany(x => x.Places == null ? null : x.Places)
.ToList();
But I receive a null exception when Places are null for their Countries object.
How can I do a null check for Places and just use return statement instead of doing select to null object, similar to what I have below?
if (allPlacesDTO.World.Countries.Places == null)
{
return;
}
Update:
My requirement was if there is no places in any of the countries just use the return statement to exit the current function without proceeding further. That was achieved by the accepted answer and Count function.
var lstAllPlaces = allPlacesDTO.World.Countries
.Where(x => x.Places != null)
.SelectMany(x => x.Places)
.ToList();
if (lstAllPlaces.Count() == 0)
{
return;
}

You can do the condition in where clause
allPlacesDTO.World.Countries.Where(x => x.Places != null)
.SelectMany(x => x.Places).ToList();
Or change the ternary operator to return new List() (it can be greedy)

Related

Getting compile time error while using lambda in C#

I am trying to use FirstOrDefault in my code but getting compile time error.
Cannot implicitly convert type Priority to bool
public class Priority
{
public string Name { get; set; }
public string Id { get; set; }
public string Default { get; set; }
}
public class ProcedureStatus
{
public string Id { get; set; }
public Priorities Priorities { get; set; }
}
public class Priorities
{
public List<Priority> Priority { get; set; }
}
foreach (Priority priority in status.Priorities.Priority)
{
if (priority.Default == "true" && grid.Priority == null)
{
grid.Priority = priority.Id;
grid.PriorityText = priority.Name;
SetPriority(gridRow);
break;
}
else if (status.Priorities.Priority.FirstOrDefault(x => x.Id == priority.Id))
priority.Default = "true";
}
How to use FirstOrDefault in my scenario.
You are using status.Priorities.Priority.FirstOrDefault(x => x.Id == priority.Id) in an if clause.
The if clause is expecting an expression that returns a bool, but your expression returns a Priority.
Aside from that, it looks like the if clause is redundant, since you are iterating the properties via a foreach and trying to get the current item from the list you are already iterating. If you just want to iterate the list, you don't need the if clause.
The way you would typically use FirstOrDefault would be like this:
var priority = status.Priorities.Priority.FirstOrDefault(x => x.Id == priorityId)`;
if (priority != null)
{
\\ ...
}
where priorityId is the ID of the priority you are looking for.
This does not seem useful inside you foreach loop though.
(Even after your question update, you still have the same if clause inside the foreach. It's just after an if/else now.)
The problem
The problem is not related to Lambda expressions (x => blablabla syntax) but related to what a specific function FirstOrDefault() returns and how to use it in an if-else-elseif scenario.
Analysis
The code you wrote could be written in two steps as:
if (priority.Default == "true" && grid.Priority == null)
{
// your if block
}
else
{
Priority firstPriority = status.Priorities.Priority.FirstOrDefault(x => x.Id == priority.Id);
// Same exact error you got... Cannot implicitly convert type Priority to bool
if (firstPriority) {
priority.Default = "true";
}
}
Now it is more easy to understand what the problem is... if, else and else if statements need a logical expression to work. That can be made either using a bool variable or doing a check/evaluation that returns true/false.
Solution
There are many different ways to address the problem:
You could use a function that directly returns a boolean like Any(), that would be useful if you do not need to use the object matching the x => x.Id == priority.Id.
You could use FirstOrDefault() but changing your else if to convert it into something that return a logic expression. This would be useful if you need to later work with that element.
Using the Any() function
else if (status.Priorities.Priority.Any(x => x.Id == priority.Id))
{
priority.Default = "true"; // Dumb question, why the Default property is not of type bool???
}
Using the FirstOrDefault()
Note: you have already been given this option in other answers. Ergwun's answer and Braulio's answer so far.
As per the documentation of the function I linked and what its name suggests, if the FirstOrDefault() function does not find anything, it returns null. So you could check it whatever it returns is null or not.
This could be done in a couple of ways. Depending on if you need to later work with whatever the function returns or not.
else if (status.Priorities.Priority.FirstOrDefault(x => x.Id == priority.Id) != null)
{
priority.Default = "true";
}
or
if (priority.Default == "true" && grid.Priority == null)
{
// your if block
}
else
{
Priority firstPriority = status.Priorities.Priority.FirstOrDefault(x => x.Id == priority.Id);
if (firstPriority != null) {
priority.Default = "true";
Console.WriteLine("The description of the priority if the Id XXXX is: " + firstPriority.Description);
}
}
Best way it's to compare if your object is not null after the research
foreach (Priority priority in status.Priorities.Priority)
{
if (grid.Priority == null)
{
grid.Priority = priority.Id;
grid.PriorityText = priority.Name;
break;
}
else if (status.Priorities.Priority.FirstOrDefault(x => x.Id == priority.Id) != null)
// You found something, do this.
}
Try it

NullReferenceException with linq in the where clause

I'm writing a program that uses Entity Framework and linq. The problem is with the query arInvArea and especially in the where clause. In WithSwimmingPool there are zero values and therefore I get null reference exception. How can I catch such exception in the where clause. Other solutions in Stackoverflow didn't help me. Thanks
private ObjectContactsRow CreateNewRow(AreaInventory arInv)
{
// Here in the where clause I get exception ! WithSwimmingPool is from type bool
var arInvArea = arInv.Area.Where(p => p.WithSwimmingPool)
.Select(p => p.Units(ReportDate))
.FirstOrDefault();
return new ObjectContactsRow()
{
areaSize = arInvArea
};
}
public partial class Area
{
public bool WithSwimmingPool => AreaArt.AreaUnit_ID == "SWMP";
}
public class ObjectContactsRow
{
public double areaSize { get; set; }
public override object[] GetExcelRow()
{
var index = 0;
Row[index++] = areaSize;
return Row;
}
}
You have to change your query to:
var arInvArea = arInv.Area.Where(p => p != null && p.WithSwimmingPool)
.Select(p => p.Units(ReportDate))
.FirstOrDefault();
Your collection may include NULL indexes and that leads to p == null in your query.
If by any chance you are using C# 8, you can enable nullable by including #nullable enable in your code. That then points out to all code segments with a null reference error potency.

c# - query nested type with LINQ

I have a model that looks like the following:
public class MyType{
public string Id {get;set;}
public string Name{get;set;}
public List<MyType> Children{get;set;}
}
and in my data I have just two level data, meaning my objects will look like:
{
MyType{"1","firstParent",
{
MyType{"2","firstChild",null},
MyType{"3","secondChild",null}}
},
MyType{"4","secondParent",
{
MyType{"5","firstChild",null},
MyType{"6","secondChild",null}}
}
}
How do I query to get MyType object with a specific Id where it might be a parent or child?
The following will return only parents.
collection.FirstOrDefault(c => c.id==id)
You can use Any with a recursive local function to find objects on any level (your data structure would seem to indicate a deeper level is possible)
bool hasIdOrChildren(MyType t, string localId)
{
return t.Id == localId || (t.Children != null && t.Children.Any(o => hasIdOrChildren(o, localId)));
}
collection.FirstOrDefault(c => hasIdOrChildren(c, id));
Or using pre C#7 syntax:
Func<MyType, string, bool> hasIdOrChildren = null;
hasIdOrChildren = (MyType t, string localId) =>
{
return t.Id == localId || (t.Children != null && t.Children.Any(o => hasIdOrChildren(o, localId)));
};
collection.FirstOrDefault(c => hasIdOrChildren(c, id));
If you are only interested in one level, you can drop the reclusiveness:
collection.FirstOrDefault(c => c.Id == id || (c.Children != null && c.Children.Any(o => o.Id == id)));
Edit
The code above gives the parent if any child has the id, you can also flatten the whole tree structure using SelectMany also with a recursive function:
IEnumerable<MyType> flattenTree(MyType t)
{
if(t.Children == null)
{
return new[] { t };
}
return new[] { t }
.Concat(t.Children.SelectMany(flattenTree));
};
collection
.SelectMany(flattenTree)
.FirstOrDefault(c => c.Id == id);
This method can be useful for any type of processing where you need to flatten the tree.
You could build a list of all MyType including children and then query on it like this :
collection.SelectMany(c => c.Children).Concat(collection).Where(c => c.id == id)
I think you're looking for
var flattenedList = IEnumerable.SelectMany(i => i.ItemsInList);
This flattens the list and gives back one list with all items in it.
In your case you need to select
collection.SelectMany(c => c.Type).Concat(collection).Where(item => item.Id == 5);
MSDN
You still got the childs in your joined parents here, but you can still erase them or ignore them.
I think, you should flatten collection using SelectMany method, then use FirstOrDefault to get element by id:
MyType selected = collection
.SelectMany(obj => new MyType[] {obj, obj.NestedList})
.FirstOrDefault(obj => obj.id == id);

How can I get an item from a generic list based on two conditions?

I have a generic list of a custom type, of which I'm trying to get back one type instance. I've tried both "FirstOrDefault" and "Where" using the two conditions which need to be true, but both of them give me the same err msg ("Operator '&&' cannot be applied to operands of type 'lambda expression' and 'lambda expression'")
Here they are:
// FirstOrDefault
UnitItemCodeItemID uicii =
unitItemCodeItemIDList
.FirstOrDefault((u => u.Unit == _unit) && (d => d.Description == desc));
// Where
UnitItemCodeItemID uicii =
unitItemCodeItemIDList
.Where((u => u.Unit == _unit) && (d => d.Description == desc));
I don't know if it's pertinent, but the class is:
public class UnitItemCodeItemID
{
public string Unit { get; set; }
public string Description { get; set; }
public string ItemCode { get; set; }
public int ItemID { get; set; }
}
You have to provide just ONE lambda:
UnitItemCodeItemID uicii = unitItemCodeItemIDList
.FirstOrDefault(obj => obj.Unit == _unit && obj.Description == desc);
Think about it this way: it's exactly like writing a foreach loop in a function. Like so:
public UnitItemCodeItemID ExtractFirst(IEnumerable<UnitItemCodeItemID> unitItemCodeItemIDList)
{
foreach(var obj in unitItemCodeItemIDList)
{
if (obj.Unit == _unit && obj.Description == desc)
return obj;
}
return null;
}
Your lamba must provide the "if" part, the rest is in the Linq implementation.
You need to provide a single lambda expression that encapsulates both conditions
.Where(p => p.Unit == _unit && p.Description == desc);
Your previous attempt was trying to combine two separate lambda expressions with an and statement.

Linq expression with nullable

Looks like stupid question, but I just dont get it.
My entity:
public class Page
{
public int Id { get; set; }
//...
public int? ParentId { get; set; }
}
In controller:
db.Pages.First(x => x.ParentId == null);
Works as expected (returns some element).
But:
int? test = null;
db.Pages.First(x => x.ParentId == test);
Throws Sequence contains no elements
What do I miss?
I believe there's an oddity around nulls with some LINQ providers. Try:
var query = db.Pages.First(x => (test != null && x.ParentId == test) ||
(test == null && x.ParentId == null));
Alternatively, use different queries for the different situations:
var query = test == null ? db.Pages.First(x => x.ParentId == null)
: db.Pages.First(x => x.ParentId == test);
Basically this is because SQL treats NULL as unequal to itself, so:
WHERE X = Y
will still fail if both X and Y are null values. Using the == null part (with a literal null) forces a conversion to ISNULL or whatever the SQL equivalent is.
I agree it's a pain, and someone else may have a better workaround, but this may help you get going.
You could do something like this as a workaround:
int? test = null;
if(test.HasValue) {
db.Pages.First(x => x.ParentId == test.Value);
} else {
db.Pages.First(x => x.ParentId == null);
}
I'm assuming since int? is actually a Nullable<int> our linq-to-entities provider isn't comparing things right.
Try this (modified according to gdoron's comment. It now is exactly what gideon posted, so please accept his instead of mine):
int? test = null;
if(test.HasValue) {
db.Pages.First(x => x.ParentId == test.Value);
} else {
db.Pages.First(x => x.ParentId == null);
}

Categories

Resources