ThinkGeo.com    |     Documentation    |     Premium Support

Collection was modified; enumeration operation may not execute

 Me again.  I am getting the above error sometimes when calling layer.QueryTools.   This is the stack trace:



02/14/2011 10:44:31


Type : System.InvalidOperationException, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089


Message : Collection was modified; enumeration operation may not execute.


Source : mscorlib


Help link : 


Data : System.Collections.ListDictionaryInternal


TargetSite : Void ThrowInvalidOperationException(System.ExceptionResource)


Stack Trace :    at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)


   at System.Collections.Generic.List`1.Enumerator.MoveNextRare()


   at System.Collections.Generic.List`1.Enumerator.MoveNext()


   at ThinkGeo.MapSuite.Core.InMemoryFeatureSource.GetFeaturesOnNotCachedBoundingBoxes(RectangleShape boundingBox, IEnumerable`1 returningColumnNames)


   at ThinkGeo.MapSuite.Core.InMemoryFeatureSource.GetFeaturesInsideBoundingBoxCore(RectangleShape boundingBox, IEnumerable`1 returningColumnNames)


   at ThinkGeo.MapSuite.Core.FeatureSource.GetFeaturesNearestToCore(BaseShape targetShape, GeographyUnit unitOfData, Int32 numberOfItemsToFind, IEnumerable`1 returningColumnNames)


   at ThinkGeo.MapSuite.Core.FeatureSource.GetFeaturesNearestTo(BaseShape targetShape, GeographyUnit unitOfFeatureSource, Int32 numberOfItemsToFind, IEnumerable`1 returningColumnNames)


   at ThinkGeo.MapSuite.Core.QueryTools.GetFeaturesNearestTo(BaseShape targetShape, GeographyUnit unitOfData, Int32 numberOfItemsToFind, IEnumerable`1 returningColumnNames)


   at EMS.ThinkGeoLibrary.MapSuite.Utilities.FeatureFinderHelper.QueryFeatures(BaseShape shape, FeatureLayer layer, IEnumerable`1 columnnNames, GeographyUnit gUnit, DistanceUnit dUnit, SpatialOperationType ops, Int32 numberOfFeaturesToReturn)


TIA.



Klaus, 
  
 Usually this exception is thrown when trying to modify a collection while in the loop of it. I didn’t find this kind of code in our source so I guess you are using some multiThreading way yourself. Could you please provide a simple sample to us so we can recreate it here? 
  
 Thanks, 
  
 James

 James, you are correct. In this case maptip that queries underlying feature source was being used while collection was being modified by a background thread.    I do not see how I can fix this from my end.  I think you guys are in a better position to fix this as you somehow have to lock the collection or make a copy before doing your queries.



Hi Klaus, 



I ran into the same issue time ago (gis.thinkgeo.com/Support/DiscussionForums/tabid/143/aff/39/aft/8600/afv/topic/Default.aspx), and fixed the issue by protecting the access with synclocks:




        SyncLock InMemoryTrackIconsFeatureLayer


          InMemoryTrackIconsFeatureLayer.InternalFeatures.Clear()


        End SyncLock


 


 


        SyncLock InMemoryTrackLinesFeatureLayer 

           InMemoryTrackLinesFeatureLayer.InternalFeatures.Add(BuoyID.ToString, newFeature) 

        End SyncLock 



Carlos.


 



Carlos, thanks for tip.  Let me try that.  In my case, I will lock editing of the collection like this: 
  
    lock (Lock) 
                 { 
                     InternalFeatures.Clear(); 
                     InternalFeatures.AddRange<Feature>(GeoFeaturesFactory.CreateGraphics(dataset.GetFeatures(), null)); 
  
                     if (InternalFeatures.Count > 0) 
                         BuildIndex();                  
                 } 
  
 But how will this prevent QueryTools from enumerating over the collection?

You are right, the GetFeaturesNearestTo code may also need to be thread sync’ed, but so far I haven’t had any other concurrency problem since I protected the parts of the code that was write-accessing the InternalFeatures.  


Hi Klaus, 
  
 You can try to lock the layer when editing the features inside it. That’ll prevent the statements like this: “layer.QueryTools.XXX” from enumerating the collection, but it also blocks sometimes. 
 Please let us know if it works for you. 
  
 Regards, 
 Tsui

Hi Tsui, 
  
  That would happen if a layer.QueryTools.XXX is enumerating when other thread locks the layer? it will block? but if the lock is released, would then the block continue? I expect it does not, right?

 Ok, will the following:



lock (this)  // where this is InMemoryFeatureLayer instance 

InternalFeatures.Clear(); 
InternalFeatures.AddRange(GeoFeaturesFactory.CreateGraphics(dataset.GetFeatures(), null)); 

if (InternalFeatures.Count > 0) 
BuildIndex(); 
}   


 


BTW, do I need to lock layer when calling BuildIndex?



 Carlos, if you lock the layer, contending threads are blocked until the layer is released.  So, one has to keep an eye on possibility for deadlock.



Hi Klaus, 
  
 The BuildIndex method does not change the collection. 
 So I believe it’s ok to put it out of the body of the lock statement. 
  
 Regards, 
 Tsui

Hi guys, 
  
  FYI: After removing the Synclocks from my Layer.BuildIndex instructions I started getting "Cannot insert items into an STR packed R-tree after it has been built" exceptions again. So they are required to be thread-protected aswell. 
  
 Carlos.

Hi Carlos, 
  
 I am sorry that I made a mistake. 
 I overlooked the fact that the operations of building index for a layer and drawing a layer share the same set of data.  
 But that is only true when the RequireIndex property is set to be true, hence we have a little trick that allows us to build index without locking. 
 The trick is to set RequireIndex to be false before building index and set it back to true after it, just in case you are interested in drawing and building index at the same time. 
  
 Regards, 
 Tsui

Hi Tsui, 
  
  Thanks for the trick, I’ll measure the time it takes to buiild the index, and will use it if it’s long. 
   
  Isn’t it necessary to have an index on each Layer in order to be able to do spatial queries? what’s the point of RequireIndex=false? 
  I agree with Klaus that you are in a better position to place thread safe protection on your update code, as we are forced to block bigger blocks of code. Wouldn’t it better if the synclocks was placed inside the BuildIndex method? 


Hi Carlos, 
  
 An index file is an enhancement; it’s not a “must-have”. And as a matter of fact, some users don’t like those extra files hanging around their shapefiles. So we have this property in case some users want to turn it off. 
 And about adding lock in the BuildIndex method, we’ll do some research on that to find out whether it may cause any performance penalties. 
  
 Regards, 
 Tsui