Why does the IF expression affect the result of my LINQ statement?

I recently worked a bit with LINQ, and thanks to the help of some StackOverflowers, I was able to get this expression to work:

var traceJob = from jobDefinition in service.JobDefinitions where jobDefinition.Id == traceGuid select jobDefinition; if (traceJob != null && traceJob.Count() == 1) { traceJob.First().RunNow(); Console.WriteLine(traceJob.First().DisplayName + " Last Run Time: " + traceJob.First().LastRunTime); } 

However, I am confused because the part that makes it work is if(traceJob.Count() ==1) . If I delete this section, I get an ObjectNullRef error saying that the traceJob enumeration traceJob .

Now, as far as I know, the if checking the score should not actually modify the results of the Linq statement correctly? Can someone explain to me why I see this behavior?

+6
source share
5 answers

No, it should not. I assume that you are faced with the case when the enumeration is really empty, and, checking the value of count> 0, First() does not work.

As a side note, Any() might be better off checking here how (depending on the underlying repository of your repository) it might be faster than Count() :

 if (traceJob != null && traceJob.Any()) { traceJob.First().RunNow(); Console.WriteLine(traceJob.First().DisplayName + " Last Run Time: " + traceJob.First().LastRunTime); } 
+6
source
 var traceJob = (from jobDefinition in service.JobDefinitions where jobDefinition.Id == traceGuid select jobDefinition).SingleOrDefault(); 

You can use singleOrDefault to check for a single result. It will return a result that matches the where or null if no match is found. If more than one match is found for your query, an exception is thrown.

This covers your conditions as tracejob == null , as well as tracejob.count == 1 .

MSDN Article

+4
source

I do not know the actual implementation of your "service", but usually linq queries actually fill in their results only on request. Therefore, Count () changes the state of traceJob, most likely populating the internal collection. And it looks like First () is not populating the internal collection or not doing it properly, even if it is normal.

+2
source

Regarding fooobar.com/questions/18529 / ...
I think using FirstOrDefault is more reasonable here.

Whenever you use SingleOrDefault, you clearly indicate that the query should produce the maximum result. On the other hand, when FirstOrDefault is used, the query can return any number of results, but you declare that you want only the first.

So, change the code to something like this:

 var traceJob = (from jobDefinition in service.JobDefinitions where jobDefinition.Id == traceGuid select jobDefinition).FirstOrDefault(); if (traceJob != null) { traceJob.RunNow(); Console.WriteLine(traceJob.DisplayName + " Last Run Time: " + traceJob.LastRunTime); } 
+2
source

I think your problem arises from the way linq expressions are executed when they are called, and not from where they are declared.

So your statement:

 var traceJob = from jobDefinition in service.JobDefinitions where jobDefinition.Id == traceGuid select jobDefinition; 

This is roughly functionally equivalent:

 IEnumerable<JobDefinition> GetJobDefinitions(YourService service, Guid traceGuid) { foreach(var jobDefinition in service.JobDefinitions) if(jobDefinition.Id == traceGuid) yield return jobDefinition; } 

Therefore, when you call traceJob.Count() , you make the equivalent of calling GetJobDefinitions(service, traceGuid).Count() , and each time you call traceJob.First() , you call the loop again.

This is probably not a problem if service.JobDefinitions can be called again and again. However, if the results change over time (for example, if tasks are added at runtime), or if subsequent runs have different results, you will have problems.

In any case, you might be better off executing the loop only once:

 var traceJobs = from jobDefinition in service.JobDefinitions where jobDefinition.Id == traceGuid select jobDefinition; // This is where the loop above actually executes - if it empty it will return null var firstJob = traceJobs.FirstorDefault(); if(firstJob != null) { firstJob.RunNow(); Console.WriteLine(firstJob.DisplayName + " Last Run Time: " + firstJob.LastRunTime); } 

Alternatively, you can force the loop to execute by converting it to a list or array:

 var traceJobsExecuted = traceJobs.ToList(); 
+2
source

All Articles