NullReferenceException thrown in ModelItemBase's GetHashCode

Aug 30, 2011 at 4:45 AM

Hi Shawn,

When I am trying to bind an ObservableCollection to a ListBox, I get the following error,


System.NullReferenceException was unhandled
  Message=NullReferenceException
  StackTrace:
       at AgFx.ModelItemBase.GetHashCode()
       at System.Collections.Generic.ObjectEqualityComparer`1.GetHashCode(Object obj)
       at System.Collections.Generic.Dictionary`2.FindEntry(Object key)
       at System.Collections.Generic.Dictionary`2.ContainsKey(Object key)
       at System.Windows.Controls.Primitives.Selector.InternalSelectedItemsStorage.Contains(Object t)
       at System.Windows.Controls.Primitives.Selector.PrepareContainerForItemOverride(DependencyObject element, Object item)
       at System.Windows.Controls.ItemsControl.MS.Internal.Controls.IGeneratorHost.PrepareItemContainer(DependencyObject container, Object item)
       at System.Windows.Controls.ItemContainerGenerator.System.Windows.Controls.Primitives.IItemContainerGenerator.PrepareItemContainer(DependencyObject container)
       at System.Windows.Controls.VirtualizingStackPanel.InsertContainer(Int32 childIndex, UIElement container, Boolean isRecycled)
       at System.Windows.Controls.VirtualizingStackPanel.InsertNewContainer(Int32 childIndex, UIElement container)
       at System.Windows.Controls.VirtualizingStackPanel.AddContainerFromGenerator(Int32 childIndex, UIElement child, Boolean newlyRealized)
       at System.Windows.Controls.VirtualizingStackPanel.MeasureOverride(Size constraint)
       at System.Windows.FrameworkElement.MeasureOverride(IntPtr nativeTarget, Double inWidth, Double inHeight, Double& outWidth, Double& outHeight)

I looked into the source code, it happens in the ModelItemBase class,

        public override int GetHashCode()
        {
            return LoadContext.GetHashCode();
        }

the LoadContext is somehow null.

I defined my collection like this,

        BatchObservableCollection _tweets = new BatchObservableCollection(4);
        public ObservableCollection Tweets
        {
            get
            {
                return _tweets;
            }
            set
            {
                _tweets.Merge(value, (t1, t2) => { return String.Compare(t1.ID.ToString(), t2.ID.ToString()); }, EquivelentItemMergeBehavior.UpdateEqualItems);
                RaisePropertyChanged("Tweets");
            }
        }

and in the Deserializer method, I have,

            public object Deserialize(TimelineLoadContext loadContext, Type objectType, System.IO.Stream stream)
            {
                // convert steam to string
                var steamReader = new StreamReader(stream);
                string json = steamReader.ReadToEnd();
                JArray array = JArray.Parse(json);
                // convert json array into tweet list using Json.Net
                var tweets = JsonConvert.DeserializeObject>(json);

                // create the model
                var model = new TimelineModel();
                model.LoadContext = loadContext;

                // populate properties
                model.Tweets = tweets;

                return model;
            }

I can see the LoadContext above is assigned with an instance of TimelineLoadContext properly, but if I put a breakpoint in the setter of LoadContext in the ModelItemBase class,

        public LoadContext LoadContext {
            get {
                return _lc;
            }
            set {
                if (!Object.Equals(value, _lc)) {

                    if (_lc != null) {
                        throw new InvalidOperationException("Identity can not be changed.");
                    }
                    _lc = value;
                    RaisePropertyChanged("LoadContext");
                }                
            }
        }

the _lc first is set to the proper instance and then GetHashCode is called and _lc is set to null for some reason...

If I just change the source code to do a null check for LoadContext in GetHashCode it works fine, but I want to know what's really causing the issue here.

With your knowledge can you think of anything that might cause this issue?

 

Thank you very much for your help!

Xin

Sep 2, 2011 at 12:15 PM

I've been scratching my head over the same issue until I suddenly realized what the problem was (at least for me - YMMV).

In my case I had a couple nested Collection properties included in my "master" VM that were loaded as part of the "master" data - i.e. all it needed was to be deserialized into their respective items classes as the data was already there.

What I had done wrong was to inherit my nested Item classes from ModelItemBase when in fact there was no need to do so. Doing this you will obviously lose out on any of the methods in ModelItemBase but all I had to do was change my inheritance to NotifyPropertyChangedBase and everything started working as intended.

Sep 2, 2011 at 12:55 PM

Hi emigrating, you just saved my day!! This must be it! :)

Thanks!
Xin

Coordinator
Sep 2, 2011 at 6:42 PM

Hi -

Sorry, I got distracted and forgot about this.

Two things:

  1. Great you found a work-around.  Makes sense to me.
  2. This is a bug, that call in GetHashCode should be checked first, I'm checking in that fix.

Thanks,

Shawn

Sep 2, 2011 at 10:13 PM

Thanks guys, really appreciated! :)