0

I have a Dictionary of type (int, < T >).

I have a helper method GetAll(); that gets me all the values as IEnumerable< T >.

I sometimes get an exception When I try to use a lambda expression to search for a value, Like this:

myDictionary.GetAll().Where(x => x.Id == myID)

The search condition is valid and it exists.

The exception I get is:

System.InvalidOperationException: Collection was modified;
 enumeration operation may not execute.
   at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
   at System.Collections.Generic.Dictionary`2.ValueCollection.Enumerator.MoveNext()
  at System.Linq.Enumerable.FirstOrDefault[TSource]
(IEnumerable`1 source, Func`2 predicate)

I checked and found that using ToList() might resolve my problem. I tried, but the issue reoccurred. Can anyone help?

EDIT - Dictionary is being called/shared by multiple threads/functions/classes simultaneously. Putting in a lock would be painful and have performance overheads. The issue is rare and random to reproduce.

EDIT2 : Used ToList(), doesn't help. Here's the GetAll method : It does use the Values internally, just has a lock object wrapped over it.

public IEnumerable<U> GetAll()
    {
        IEnumerable<U> values;
        lock (lockObj)
        {
            values = myDictionary.Values;
        }
        return values;
    }
Uday Mehta
  • 109
  • 2
  • 13
  • Why are you using `GetAll()` and not the inbuilt `Values`? What else is `GetAll()` doing? – InBetween Jul 04 '17 at 08:18
  • Here's the GetAll method : It does use the Values internally, just has a lock object wrapped over it. public IEnumerable GetAll() { IEnumerable values; lock (lockObj) { values = myDictionary.Values; } return values; } – Uday Mehta Jul 04 '17 at 08:22
  • 1
    The problem is that `Values` is an `IEnumerable`, so its *lazy*. The underlying enumeration isn't enumerated until someone or something iterates through it. The error is thrown because when the enumeration is finally iterated, something is modifying the dictionary simultaneously from another thread. To get around this, call `ToList()` inside the `lock` to force the iteration at that point in time creating a snapshot of `Values` that can't be othered by any subsequent modifications in the dictionary. – InBetween Jul 04 '17 at 08:41
  • Of course this has two problems you need to contend with: 1. Eagerly enumerating `Values` can have a performance cost that might not be acceptable. 2. Your `Values` snapshot will be randomly stale so any consumer iterating through it might not be getting the latest version of the underlying dictionary. – InBetween Jul 04 '17 at 08:45
  • @InBetween : Thanks, I put in the ToList() on the values inside the lock, and that seems to have fixed the issue ! Any Ideas though on how I could go about addressing the two problems you have just mentioned ? I wasn't even aware of these, but now that you have mentioned them , I'll need to at least have some sort of a workaround. Could there be any alternative solution ? – Uday Mehta Jul 10 '17 at 14:05

0 Answers0