-1

I have the following code:

// OnlineAccountCategory is a class
var itemsByCategory = Items.GroupBy(x => x.OnlineAccountCategory); 
foreach (var items in itemsByCategory)
...

Items is a list that contains 1 item. Item has a OnlineAccountCategory which is not null. But when I'm trying to go through itemsByCategory I receive a NullReferenceException. In the debug mode I can see that itemsByCategory is a GroupedEnumerable but inside of it if I expand the Results View it will tell that 'An object reference is not set to an instance of an object'.

I don't quite understand what's the problem with this code as GroupBy() goes fine without any exception but then breaks in the foreach loop.

Ivan Yurchenko
  • 3,762
  • 1
  • 21
  • 35

2 Answers2

2

The problem here is that OnlineAccountCategory is a reference type property (and not a string or something like that). Grouping by reference types uses method GetHashCode() of a class by which the grouping is occuring.

And the key thing is if the GetHashCode() method throws an exception (for instance, if it relies on working with a property of our object which is null) the GroupBy() method won't fail. It will just return a GroupedEnumerable that contains an exception inside for some reason.

// Trying to add this to watch:     
Items.First().OnlineAccountCategory.GetHashCode()   
// Receiving: 
'Items.First().OnlineAccountCategory.GetHashCode()' threw an exception of type 
'System.NullReferenceException' int {System.NullReferenceException}

Hence, when we are trying to access an element of the collection, it falls with NullReferenceException.

To solve this we have two approaches:

  1. Override the GetHashCode method of our OnlineAccountCategory so it will not fail on incorrect values and instead of throwing an exception will return something like 0.

  2. Group by some value type instead (for instance, OnlineAccountCategory.Id). It will group by it and hence won't fail.

Update: The problem will occur only when the GetHashCode() method was already implemented incorrectly (which was implemented in the base class in my case). The default reference comparer will work with that as expected.

The GetHashCode() method for me looks like this:

public override int GetHashCode()
{
    // Title is a string
    return Title != null ? Title.GetHashCode() : 0;
}

So I don't really understand why does it break with a NullReferenceException here.

Ivan Yurchenko
  • 3,762
  • 1
  • 21
  • 35
  • 1
    Your first "solution" absolutely make no sense. If you don't override `GetHashCode`, the default reference equality comparer implementation is used, which never throw NRE. Looks like your issue is the opposite - you **did** override `GetHashCode` and implemented it wrongly, which is simply a bug. Also, regarding "is a reference type (and not a string or something like that)" - FYI `string` is also a **reference type**. – Ivan Stoev Jun 02 '16 at 15:15
  • "this problem with occur only with reference types such as classes, not strings" - the problem does _not_ occur on _any_ type _unless you implement `GetHashCode` incorrectly_. There's nothing wrong with grouping by a reference type in general. – D Stanley Jun 02 '16 at 15:24
  • Yes, I agree with you, maybe I was somewhat unclear. That's what I wrote: "is if the `GetHashCode` method throws an exception (for instance, if it relies on working with a property of our object which is null)" So I meant if the implementation is incorrect and throws an exception we will get such situation here. I understand that string is a reference type so that's why I mentioned that the problem will only occur with classes where the implementation of `GetHashCode` could be incorrect (not with reference types such as strings where we can't make an incorrect implementation of `GetHashCode`. – Ivan Yurchenko Jun 02 '16 at 15:31
2

I'm glad you got the problem solved but I don't think you fully understand the reason for the error.

I don't quite understand what's the problem with this code as GroupBy() goes fine without any exception but then breaks in the foreach loop.

The reason you're not seeing the error until foreach is executed is deferred execution. GetHashCode is not called until you try to enumerate the collection. If GetHashCode is throwing a NullReferenceException then you won't know that until you execute the query with foreach, which is when the groups are actually created.

Certainly the root cause is your incorrect GetHashCode method, which can easily be detected in unit tests.

Note that it's perfectly fine to group by reference types without overriding GetHashCode, since any null values will just be put into a group with a null key (Linq will not try and call GetHashCode on the null reference). You only need to override GetHashCode when you want to define equality for different instances based on values of the instance, instead of using reference equality by default.

D Stanley
  • 149,601
  • 11
  • 178
  • 240
  • I think you're right about deferred execution, thanks for the clarification. The problem for me was that my OnlineAccountCategory property was NOT a null so the implementation of GetHashCode() was failing with an exception. – Ivan Yurchenko Jun 02 '16 at 15:26
  • deferred execution could be a problem at least in my case, putting .ToList() before foreach has solved my problem. Will have to check for a couple of day to see if the error has goes away. – Ariwibawa Aug 14 '19 at 10:55