Linq expression with nullable - c#

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);
}

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

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);

null check nested objects before using SelectMany

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)

Correctly distinguish between bool? and bool in C#

I am trying to find out if a variable is either a simple bool or a Nullable<bool>.
It seems that
if(val is Nullable<bool>)
returns true for both bool and Nullable<bool> variables and
if(val is bool)
also returns true for both bool and Nullable<bool>.
Basically, I am interesting in finding out if a simple bool variable is true OR if a Nullable<bool> variable is not null.
What's the way to do this?
Here is the full code:
List<string> values = typeof(InstViewModel).GetProperties()
.Where(prop => prop != "SubCollection" && prop != "ID" && prop != "Name" && prop != "Level")
.Select(prop => prop.GetValue(ivm, null))
.Where(val => val != null && (val.GetType() != typeof(bool) || (bool)val == true)) //here I'm trying to check if val is bool and true or if bool? and not null
.Select(val => val.ToString())
.Where(str => str.Length > 0)
.ToList();
The InstViewModel object:
public class InstViewModel
{
public string SubCollection { get; set; }
public string ID { get; set; }
public string Name { get; set; }
public string Level { get; set; }
public bool Uk { get; set; }
public bool Eu { get; set; }
public bool Os { get; set; }
public Nullable<bool> Mobiles { get; set; }
public Nullable<bool> Landlines { get; set; }
public Nullable<bool> UkNrs { get; set; }
public Nullable<bool> IntNrs { get; set; }
}
The point of my code here is to find out if all of the object's values are null (more specifically, to find out any values that are not null and save them in a List<string>). This presents a complication in the lambda expression, however, when trying to distinguish between bool and bool? types in my object (second Where statement).
Additionally, since the object contains some string types as well, I am trying to exclude those in my first .Where statement (which I am probably not doing right at present as it doesn't seem to be working). But my main goal is to distinguish between bool and bool? types.
There is a simple way to check whether a variable is declared as T or T?:
private static bool IsNullable<T>(T val)
{
return false;
}
private static bool IsNullable<T>(T? val)
where T : struct
{
return true;
}
Usage:
bool? val = false;
if (IsNullable(val))
{
...
}
EDIT
Try the following code for edited question:
var boolProps = typeof (InstViewModel).GetProperties()
.Where(prop => prop.PropertyType == typeof(bool))
.Select(prop => (bool)prop.GetValue(ivm, null))
.Select(v => v ? v.ToString() : String.Empty);
var nullableBoolProps = typeof(InstViewModel).GetProperties()
.Where(prop => prop.PropertyType == typeof(bool?))
.Select(prop => (bool?)prop.GetValue(ivm, null))
.Select(v => v.HasValue ? v.ToString() : String.Empty);
List<string> values = boolProps.Concat(nullableBoolProps)
.Where(str => str.Length != 0)
.ToList();
Code for getting class instance values:
// create class instance
InstViewModel model = new InstViewModel()
{
Uk = true,
UkNrs = false,
};
// check all boolean fields are false or null
bool isAllNullOrFalse = (from property in typeof(InstViewModel).GetProperties()
let type = property.PropertyType
let isBool = type == typeof(bool)
where isBool || type == typeof(bool?)
let value = property.GetValue(model)
select value == null || (isBool && bool.Equals(value, false))).All(e => e);
Console.WriteLine("All values are null or false = {0}", isAllNullOrFalse);
typeof(InstViewModel).GetProperties()
.Select(prop => prop.GetValue(ivm, null))
At this point, you have a sequence of type object. Each element of that sequence will be an object that can be one of:
An instance of a reference type.
A boxed instance of a value type.
null.
The null case can happen either because you had a null value for a property which was of reference type, or a null value for a property that was a nullable value type; there's no way to tell the difference at here. Likewise there's no way to tell the difference between a boxed bool that came from a bool value or a boxed bool that came from a bool? value.
You need to examine the type of the property, not the property's value:
isNullableProperty = property.PropertyType.IsGenericType
&& property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>);
But to just filter to only bool and bool? then:
typeof(InstViewModel).GetProperties()
.Where(
prop => prop.PropertyType == typeof(bool)
|| prop.PropertyType == typeof(bool?))
You can distinguish the boolean and nullable boolean properties before you evaluate them. Then there is no need to worry about whether they evaluate to a bool or a Nullable<bool>:
var nullableBooleanProperties = typeof(InstViewModel).Where(prop => prop.PropertyType == typeof(bool?));
Then you can simply write these out to a list of strings:
var values = nullableBooleanProperties.Select(prop => prop.GetValue(ivm)).Where(val => val != null).Select(val => val.ToString());
Putting these together gives:
var values = typeof(InstViewModel).Where(prop => prop.PropertyType == typeof(bool?))
.Select(prop => prop.GetValue(ivm)).Where(val => val != null)
.Select(val => val.ToString())
.ToList();
which gives you the list you are looking for.
According to your question
Basically, I am interested in finding out if a simple bool variable is true OR if a Nullable variable is not null.
to tell if a simple boolVariable is true
if(boolVariable){
//bool variable, not nullable
}
to tell if your nullableVariable is not null
if(nullableVariable.HasValue){
//a nullable variable is not null
}
to tell if nullable bool variable is true or/and not null, use the ?? operator
if(variable??false){
//then I'm sure that this is not null and has value=true
}
So in definitive you can use the following code for both nullable bool and bool variables
if(variables!=null &&variables!=false){/*This may give a warning message but it works*/}
or
if(((bool?)variable)??false){
/*variable is not null and is true*/
}
This is a response to the initial question - ignore this.
When you "box" a nullable value (so you put it in an object), it is transformed in its underlying type (bool in your case) or in null... So if you have
bool? value = true;
object value2 = value;
now value2.GetType() == typeof(bool)
Try this
List<string> values = typeof(InstViewModel).GetProperties()
.Select(prop => new { N = prop.Name, T = prop.PropertyType, V = prop.GetValue(ivm, null) })
.Where(prop => prop.N != "SubCollection" && prop.N != "ID" && prop.N != "Name" && prop.N != "Level")
.Where(val => (val.V != null && val.T.IsAssignableFrom(typeof(Nullable<bool>))) || Convert.ToBoolean(val.V))
.Select(val => val.V.ToString())
.Where(str => str.Length > 0)
.ToList();
Try this:
((bool?)val).HasValue
This will return true, if val is a bool or if val is a bool? which value is not null.
On the other hand,
!((bool?)val).HasValue
will only return true if val is bool? and its value is null.
Will not that test suffice in your case?

action is always return null

My action is :
[HttpPost]
public ViewResult SearchPost(FormCollection frm)
{
IList <post> p =db.posts.Include("user").ToList();
if (Request.Form["area"] != null)
{
if ((p!=null) && (p.Any()))
{
p =p.Where(a=>a.area==Request.Form["area"]).ToList();
}
}
if (Request.Form["floor"] != null)
{
if ((p!=null) && (p.Any()))
{
p = p.Where(a => a.floor ==
Request.Form["floor"]).ToList();
}
}
if (Request.Form["garage"] != null)
{
if ((p!=null) && (p.Any()))
{
p = p.Where(a => a.garage ==
Request.Form["garage"]).ToList();
}
}
return View(p);
}
it has shown no errors. but always return null. it should return filtered post objects or simply all posts without filtering. is there any problem ?? i can't find it .
I don't know the full signatures of the types that you are dealing with, but perhaps try this code and see if you get any better results:
[HttpPost]
public ViewResult SearchPost(FormCollection frm)
{
var area = Request.Form["area"];
var floor = Request.Form["floor"];
var garage = Request.Form["garage"];
return View(db.posts.Include("user")
.Where(a => area == null || a.area == area)
.Where(a => floor == null || a.floor == floor)
.Where(a => garage == null || a.garage == garage).ToList());
}
(This is essentially a rewrite of your code down into a single query that might help you with debugging.)

Categories

Resources