ThinkGeo.com    |     Documentation    |     Premium Support

Getting "The FeatureSource is not open" randomly

Hello,


I have the following methods in a MVC application, which both gets called periodically by ajax (first RefreshVesselMarkers then AddVesselTrails):



public IEnumerable<VesselPosition> RefreshVesselMarkers(RectangleShape currentExtent = null)
{
    var featureLayer = _map.DynamicOverlay.Layers[VesselMarkersOverlayName] as InMemoryFeatureLayer;

    lock (_syncObject)
    {
        featureLayer.FeatureSource.Open();
        featureLayer.FeatureSource.BeginTransaction();
        featureLayer.InternalFeatures.Clear();

        _map.Projection.Open();
        var extent = _map.Projection.ConvertToInternalProjection(currentExtent ?? _map.RestrictedExtent ?? _defaultRestrictedExtent);

        var positions = new List<VesselPosition>();
        foreach (var vesselPosition in _positionService.GetVesselPositions(extent, _customerGroupId))
        {
            var feature = _map.Projection.ConvertToExternalProjection(CreateFeatureFromVesselPosition(vesselPosition));
            featureLayer.FeatureSource.AddFeature(feature);

            var point = feature.GetShape() as PointShape;
            if (point != null)
            {
                vesselPosition.Lon = point.X;
                vesselPosition.Lat = point.Y;
                positions.Add(vesselPosition);
            }
        }

        featureLayer.FeatureSource.CommitTransaction();
        _map.Projection.Close();
        featureLayer.FeatureSource.Close();

        return positions;
    }
}




public void AddVesselTrails(IEnumerable<int> vesselIds, TimeSpan? ageLimit)
{
    AssertMapIsInitialized();

    var layer = _map.DynamicOverlay.Layers[VesselTrailsOverlayName] as InMemoryFeatureLayer;

    lock (_syncObject)
    {
        layer.FeatureSource.Open();
        layer.FeatureSource.BeginTransaction();
        layer.InternalFeatures.Clear();

        _map.Projection.Open();

        Action<IEnumerable<VesselPosition>> addFeature = positions =>
        {
            var feature = new Feature(new LineShape(positions.Select(o => new Vertex(o.Lon, o.Lat))));
            layer.FeatureSource.AddFeature(_map.Projection.ConvertToExternalProjection(feature));
        };

        if (vesselIds.Count() > 1)
        {
            var positions = _positionService.GetHistoricalPositionsForVessels(vesselIds);

            foreach (var position in positions)
                addFeature(position.Value);
        }
        else
        {
            var positions = _positionService.GetHistoricalPositionsForVessel(vesselIds.First()).ToList();
            if (positions.Count > 1)
                addFeature(positions);
        }

        _map.Projection.Close();
        layer.FeatureSource.CommitTransaction();
        layer.FeatureSource.Close();
    }
}


From time to time, I get this exception:


System.InvalidOperationException: The FeatureSource is not open.   Please call the Open method before calling this method.     at ThinkGeo.MapSuite.Core.FeatureSource.AddFeature(Feature feature)



This does not make any sense to me, as you can see I am opening and closing the FeatureSource in each method. I even added a lock in case there was some problem with concurrency.



Does anyone have an idea what can be wrong?



Hi Sindre,



I’m sorry but I can’t use your code directly because some unknown functions in it. I can reproduce your situation by myself following your description. 


When I call redraw on client side after complete one of the server function by Ajax, I get the issue. So I guess there is something wrong when client side request redraw but the server side is handling the Ajax request, this two requests might access one feature source at the same time, it will cause the error. 


Is it possible to provide your project to us?


Thanks,


Edgar




Thanks. That indeed seemed to be the issue. I was calling map.getDynamicOverlay().redraw() in the callback for the first request, while the second request was still accessing the feature source on the server. I modified the code to only call redraw after the second request completes and I have not gotten the error since. 
  
 Is this how it is supposed to work, or is it a bug? Should a client-side redraw really affect the server side code?

Sindre, 
  
 This is not a bug, and the “client-side” map.getDynamicOverlay().Redraw() also will request server, so it is totally a concurrency issue, please make sure no concurrent server requests in your code.  
  
 Regards, 
 Edgar

Ok, thanks. I guess that makes sense. So how do I ensure map.getDynamicOverlay().Redraw() does not get called concurrently. I need some way to synchronize these calls, as some actions by the user may trigger a redraw. If one is already processing it will fail. Is there a callback event or something for this method?



Sindre, 
  
 There is no any callback for this situation. there are two ways I think you should try:  
  
 1. Make the “_syncObject” static and try it again. 
 2. Pause all the timers which would do the Ajax call before calling the redraw method, and restart it if the redraw method returns true. 
  
 Regards, 
 Edgar 


Edgar, 
  
 Thanks for your advise. One question: do you know in what situation will the “redraw” method return false? I see from the OpenLayers docs that it does return a boolean, but I don’t see how the redraw could fail?

Hi Sindre, 
  
 Here are some situations I guessed where the “redraw” function return ‘false’: 
 1.  The map is empty 
 2. Current extent is out of range 
 3. the center of the map is not configured 
 4. the layer is invisibility. 
  
 More detailes please check the “layer.js” of “openlayersLayer” at line around 543. 
  
 Thanks, 
 Johnny

Hi, 
  
 Any advice on how to avoid the client side to issue Ajax request when the current request is not completed yet? 
  
 I was wondering if it could make sense to have on the client side an Ajax request queue that will be processed in FIFO mode,  this could put to sleep the Ajax concurrency issue once for all? 
  
 Jm. 
  


Hi Jean-marie, 


Here is a simple workaround which might be useful. I guess you can try to set a flag at callback to represent whether the current request have gotten the response. If the flag is false, then any other Ajax call would not work. 
 
Besides, as the Ajax request queue, we did a research and seems the AjaxQ which is a Jquery plugin can handle it. You can learn more detailed from here  archive.plugins.jquery.com/project/ajaxq.
 

Thanks,


Johnny



 


Hi Jm.
Here is a simple work around which might be helpful. I think you can try setting a flag at the callback to represent whether current request have gotten the response. If it’s false, then any other Ajax Call shouldn’t be called.
Additionally,    for the Ajax request queue issue, we did a research on it and seems like the AjaxQ which is a Jquery plugin can handle it, please get more details at archive.plugins.jquery.com/project/ajaxq .
Regards,
Johnny