Skip to content

InvalidOperationException: Enumerator was modified in SequencedHashMap.OrderedEnumerator.MoveNext #3723

@cremor

Description

@cremor

My application code contains a method that looks like this (simplified):

public bool ContainsLoadedEntityEqualTo(object entity)
{
   return session
      .GetSessionImplementation()
      .PersistenceContext
      .EntityEntries
      .Keys
      .Cast<object>()
      .Contains(entity);
}

session.GetSessionImplementation().PersistenceContext.EntityEntries is of type NHibernate.Util.IdentityMap.
The private field IdentityMap.map is of type NHibernate.Util.SequencedHashMap.
IdentityMap.Keys, which forwards to SequencedHashMap.Keys, is of type NHibernate.Util.SequencedHashMap.KeyCollection.
In some rare cases enumerating this SequencedHashMap.KeyCollection throws this exception:

System.InvalidOperationException: Enumerator was modified
   at NHibernate.Util.SequencedHashMap.OrderedEnumerator.MoveNext()
   at System.Linq.Enumerable.<CastIterator>d__97`1.MoveNext()
   at System.Linq.Enumerable.Contains[TSource](IEnumerable`1 source, TSource value, IEqualityComparer`1 comparer)
   at System.Linq.Enumerable.Contains[TSource](IEnumerable`1 source, TSource value)
   at MyClass.ContainsLoadedEntityEqualTo(object entity)

So the Contains call iterates over EntityEntries.Keys and that throws an exception in SequencedHashMap.OrderedEnumerator.MoveNext().
I've also tried to add a ToList call before the Contains call but that doesn't change anything other than the call stack showing ToList instead of Contains.

This is the code that throws:

public bool MoveNext()
{
if (_parent._modCount != _expectedModCount)
{
throw new InvalidOperationException("Enumerator was modified");
}

I don't know how to reproduce this. I've never seen the exception in my dev environment, only in log files from the production system.

As a quick and ugly workaround I tried to wrap the call in a try-catch-block which retries a few times if it fails. But that doesn't fix it. If the first iteration throws the exception, then any further iteration throws too. That's also why I think that this is not a timing/threading problem.

Could it be that iterating over EntityEntries.Keys somehow sometimes triggers lazy loading that then modifies the EntityEntries collection?

I don't know with which NHibernate version I first saw that exception. But it was at least back with version 5.5.2.
I haven't deployed NHibernate 5.6.0 yet, but I can confirm that I also saw the exception with 5.5.3.

I've found the bugs #1216 and #3355 which might be related. But while the exception and the exact source are the same, the call stack is different for me.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions