Using Equals with LINQ on an IEnumerable - c#

this code :
var allSubjectsForAStudent = GetAllSubjects<Subject>(studentId);
returns an
IEnumerable<Subject>
and I can see bunch of subjects returned in the debugger.
Want to check for a particular Subject doing a case insensitive comparison.
This is the code that I have:
var studentSubjects = allSubjectsForAStudent.Where(s => s.Name.Equals(subjectName, StringComparison.CurrentCultureIgnoreCase));
'subjectName' is a parameter that the method will recieve.
When this line executes I get the 'Object not set to an instance of an object' error.
So what I want to do is a CASE INSENSITIVE search and return the first item when there are more than one and return an empty collection when there are none.
Any clues?
Edit 1
The answers suggest that there can be an entry in the first collection which might have a 'null'. While the observation is true the program makes sure that the 'Subject Name' can not be a null value. Hope this helps.
Thanks in advance.

You could try:
var studentSubjects = allSubjectsForAStudent.Where(s => !string.IsNullOrWhiteSpace(s.Name) && s.Name.ToUpperInvariant() == subjectName.ToUpperInvariant()).FirstOrDefault();
This will either set studentSubjects to null or the first instance in the IEnumerable that matches.
You're getting a NullReferenceException when you call
s.Name.Equals(subjectName)
for an object where s.Name is null.

It will :
if(allSubjectsForAStudent!=null && !string.IsNullorEmpty(subjectName))
var studentSubjects = allSubjectsForAStudent.Where(s => s.Name.Equals(subjectName, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();

Related

.Single(c.=>c.Id==id) method returns NULL

I have a problem with the code below. The first seach (loadToUpdate1) is working. It returns the value.
CustomerId also has a value (in debug mode it is 4). The customer with such Id exists. It's in there (in debug mode I can see this customer with Id 4 in the list Customers).
But why does customerToUpdate1 return NULL? At first I thought that they (Id and CustomerId) are different data types. But they are both integers. Why does .Single(c => c.Id == customerId) not work?
Any idea?
UPD. I tried First instead of Single, but the result is the same (NULL).
var loadToUpdate1 = _context.LoadConfirmations.Include(c=>c.Customer).Single(c => c.Id == loadFormViewModel.LoadConfirmation.Id);
var customerId = loadToUpdate1.CustomerId;
var customerToUpdate1 = _context.Customers.Single(c => c.Id == customerId);
In your debug images, you haven't actually executed the query yet. You need to step to the next line for the value of someCustomer to be set.
If someCustomer could be null than you need to use SingleOrDefault.
First will return the first result of 1:N. Throws an exception if there are 0 results
FirstOrDefault will return null, or the first result of 0:N
Single will return the first result of 1:1. Throws an exception if there are 0 or more than 1 results
SingleOrDefault will return null, or the first result of 0:1
I didn't know how to debug properly. I didn't know that the Visual Studio debugger doesn't show the assigned value when the assignment happens on the break point. Thank you, Caius Jard.

How to check if IQueryable element/ item contains a property in C#?

I have an IQueryable as follows
var issues = from i in jira.Issues.Queryable
where i.Project == project.Key
orderby i.Created
select i;
I am trying to get the count where issue type is Bug like this:
TotalIssues = issues.ToList().Where(i => i.Type.Name == "Bug").Count();
The problem here is there are some issues without a type. How can I check if the issue type exists?
You can try null propagation ?. in order to have null for Name if any item or item.Type are null:
TotalIssues = issues
.AsEnumerable() // Linq to objects from now on; no need in materialization
.Count(item => "Bug".Equals(item?.Type?.Name)); // just count
You don't want issues without type. You can keep code simple by not taking issues without type into account.
var bugsCount = issues.AsEnumerable()
.Where(issue => issue.Type != null)
.Count(issue => issue.Type.Name == "Bug");
You can chain as much .Where clauses as you wish, items will be still enumerated only once.
What do you mean? Some issues don't have Type Property? Or Some issues's property Type is null?
If it is some issues don't have Type Property, I think you can specify issues's class or interface. Or you can use reflection.
If it is some issues's property Type is null, you can check Type is not null first.
From your comment, I think i may be null too.
i!=null && i.Type!=null && i.Type.Name=="Bug"

Null Propagation

When the linq query condition is not met, I'd expect a null to be returned from questions.FirstOrDefault() - but instead, an exception
Sequence contains no matching element
is thrown. Any ideas why?
var firstQ = questions.FirstOrDefault(a =>
a.Answers.Single(x => x.CourseAssignmentId ==
courseAssignmentId)?.Score == null) ?? questions.FirstOrDefault();
That's the difference between Single and SingleOrDefault.
Single throws an exception if there's any number of items different than 1 that match your predicate.
You should be using FirstOrDefault() instead. BTW you can combine the condition probably like
a.Answers.Single(x => x.CourseAssignmentId == courseAssignmentId && x.Score == null)
As others have already mentioned, it's the expected behavior of Enumerable.Single.
Anyway, it looks like an XY problem. Probably you should store the last scored question somewhere (e.g a dictionary).
That is, you could refactor your code as follows:
var assignmentScoredQuestionMap = new Dictionary<int, Question>();
// Fill the whole dictionary:
// You need to add for which assignment identifier you've answered a question
int assignmentId = 384;
// If the whole assignment exists, you set lastScoredQuestion, otherwise
// you set it to first question.
if(!assignmentScoredQuestionMap.TryGetValue(assignmentId, out var lastScoredQuestion))
lastScoredQuestion = questions.FirstOrDefault();

Check if query to DB is empty

The action method in the controller receive a value, like a couple of letters. The code checks if the table contains any of this letters. I'm using this code for that task:
var result = db.People.Where(b => b.Name.ToUpper().Contains(filter.ToUpper()));
But how can I check if the result variable is empty or null, when there are not any matching letters? I tested this, but it's not working!
If(result == ""){
// Do something
}
I would like to use Viewbag to send a message that there was no match or perhaps do this check in the View. I'm using this with some AJAX and Partial Views and it's working perfect, but I just want show a message if there are not any matches. What is the best way to check if the result value is empty or null?
The simplest way would be by using !result.Any():
var result = db.People.Where(b => b.Name.ToUpper().Contains(filter.ToUpper()));
If(!result.Any()){
// Do something
}
From MSDN on IEnumerable:
Any()
Determines whether a sequence contains any elements.
Fits exactly what you need.
Try this, Using FirstOrDefault it will give you the first record from the result, else if no result is yielded from the query it returns default value that is null,
var result = db.People.Where(b => b.Name.ToUpper().Contains(filter.ToUpper())).FirstOrDefault();
If(result == null){
// Do something
}
Or if you want to use this result, to manipulate something in your code then you can use the ToList(). It will return you list of values and if the query didnt yield anything then the list count will be 0.
var result = db.People.Where(b => b.Name.ToUpper().Contains(filter.ToUpper())).ToList();
If(result.Count == 0){
// Do something
}
Your code is not working because the code is returning an object not a string if you want to return a string then you have to use the "Select" clause to select a specific field and if you want to check in the same code then modify :
var result = db.People.Where(b => b.Name.ToUpper().Contains(filter.ToUpper())).ToList();
if(result != null && result.Count > 0)
it will work for you.
For IEnumerable we can use Any(), your code will can be written as
if(!db.People.Where(b => b.Name.ToUpper().Contains(filter.ToUpper())).Any() {
// do some thing
}

What does LINQ return when the results are empty

I have a question about LINQ query. Normally a query returns a IEnumerable<T> type. If the return is empty, not sure if it is null or not. I am not sure if the following ToList() will throw an exception or just a empty List<string> if nothing found in IEnumerable result?
List<string> list = {"a"};
// is the result null or something else?
IEnumerable<string> ilist = from x in list where x == "ABC" select x;
// Or directly to a list, exception thrown?
List<string> list1 = (from x in list where x == "ABC" select x).ToList();
I know it is a very simple question, but I don't have VS available for the time being.
It will return an empty enumerable. It won't be null. You can sleep sound :)
You can also check the .Any() method:
if (!YourResult.Any())
Just a note that .Any will still retrieve the records from the database; doing a .FirstOrDefault()/.Where() will be just as much overhead but you would then be able to catch the object(s) returned from the query
var lst = new List<int>() { 1, 2, 3 };
var ans = lst.Where( i => i > 3 );
(ans == null).Dump(); // False
(ans.Count() == 0 ).Dump(); // True
(Dump is from LinqPad)
.ToList returns an empty list. (same as new List<T>() );
In Linq-to-SQL if you try to get the first element on a query with no results you will get sequence contains no elements error. I can assure you that the mentioned error is not equal to object reference not set to an instance of an object.
in conclusion no, it won't return null since null can't say sequence contains no elements it will always say object reference not set to an instance of an object ;)
Other posts here have made it clear that the result is an "empty" IQueryable, which ToList() will correctly change to be an empty list etc.
Do be careful with some of the operators, as they will throw if you send them an empty enumerable. This can happen when you chain them together.
It won't throw exception, you'll get an empty list.

Categories