5

About once a month, we've been running into a bizarre error that I have no explanation for. The error is this:

System.IndexOutOfRangeException: Index was outside the bounds of the array.
at System.Collections.Generic.List`1.Enumerator.MoveNext() at 
System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source, Func`2   predicate)

Here's the code where the error is happening. This method is called in the constructor of MyObject:

// pull a list of MyObjects from the cache so we can see if this one exists
List<MyObject> MyObjectList= System.Web.HttpRuntime.Cache["MyObjects"] as   List<MyObject>;
if (MyObjectList!= null)
{
     // this is where the error happens.  Just getting an object out based on its id
     MyObject obj = MyObjectList.FirstOrDefault(m => m.Id == this.Id);

     if(obj != null){
         // great, it already exists in the cache
     }
     else{
         // doesn't exist in the cache, query the database and then add it to the cache
          //add it to the cache after loading from db
          MyObjectList.Add(this);
          System.Web.HttpContext.Current.Cache.Insert("MyObjects",MyObjectList);
     }
}
else{
    // instantiate a new list, query the db for this object, add it to the list and add the list to the cache
    MyObjectList= new List<MyObject>();

    //add it to the cache after it was loaded from the db
    MyObjectList.Add(this);
    System.Web.HttpContext.Current.Cache.Insert("MyObjects",MyObjectList);
}

When the error happens, it will happen 100% of the time when this method runs (which is a lot) until I recycle the app pool, which fixes it. This suggests to me that it has something to do with the caching part of this, but it still doesn't make sense to me, since once I pull MyObjectList from the cache there is nothing modifying it, but it seems like the only way this error could happen would be if MyObjectList was somehow being modified while firstordefault was happening.

The error is so infrequent and not predictable that we haven't been able to recreate it, so any help would be greatly appreciated.

maembe
  • 1,270
  • 1
  • 13
  • 25
  • Show the code where you fill the list. – Tim Schmelter Feb 15 '16 at 16:37
  • I added the the code that's inserting MyObjectList into the cache. I assume that's what you're asking for @TimSchmelter – maembe Feb 15 '16 at 16:45
  • 3
    "since once I pull MyObjectList from the cache there is nothing modifying it" - how are you ensuring that? I can see you adding to it within the code you've given, for example... – Jon Skeet Feb 15 '16 at 16:47
  • @JonSkeet I'm not doing anything to ensure that, but the code that modifies the MyObjectList happens after FirstOrDefault is called. – maembe Feb 15 '16 at 16:50
  • 2
    @maembe: Yes, but the whole point about it being modified concurrently is that it's likely to be in a different thread... – Jon Skeet Feb 15 '16 at 16:55
  • 5
    Basically, you're trying to use a type which isn't thread-safe (`List`) in a multi-threaded environment without any synchronization. Don't do that :) – Jon Skeet Feb 15 '16 at 16:56
  • You could [`lock`](http://stackoverflow.com/questions/39112/what-is-the-best-way-to-lock-cache-in-asp-net) it. But the best way is to avoid the cache. Multithreading issues like this are one disavdantages, if data changes in the database you are using outdated data. In most times simply selecting it from the database is the most scalable aproach. – Tim Schmelter Feb 15 '16 at 17:08
  • 1
    Change the code from `MyObject obj = MyObjectList.FirstOrDefault(m => m.Id == this.Id);` to `MyObject obj=null; foreach(var m in MyObjectList) if (m.Id==this.Id) obj=m;` and I'm going to assume that you still get an error. It is more likely that your List object has somehow become corrupted and it's count and actual length aren't the same. You get the error because you are trying to iterate the corrupted list, not because of anything LINQ related. – Robert McKee Feb 15 '16 at 19:24
  • I feel for you, this exact thing happened to me. I was using System.Web.HttpContext.Current.Cache in a multi threaded environment trying to use lock where appropriate and thread-safe containers (see the System.Collections.Concurrent namespace). But still would get cache/thread errors. We ended up scrapping the HttpContext cache and just implemented an in memory cache per web service call (ie. no static variables). We are still multi-threading. So far it's worked without problems for about 5 months now. – goku_da_master Sep 21 '16 at 23:17

1 Answers1

1

if (MyObjectList!= null) maybe try like this if (MyObjectList!= null && MyObjectList.Any(m => m.Id == this.Id)

and FirstOrDefault will be just First maybe sometimes u have an empty list and u have fallback for this on the else block

Alex Banu
  • 28
  • 3