I'm experiencing an issue trying to delete a feature which was just created. This is all while working online using iOS (haven't tested yet on Android or UWP).
The scenario is this:
If I query the table to get all of the records again, I can then find the one I want using other attributes and delete it, but this only works after using another query. Any way around this? Should RetryLoadAsync() rehydrate the feature as it is on the server? Seems like there needs to be a way to get the server feature after it is created, but I don't see any way to do that.
Any ideas or thoughts would be appreciated.
Hi Chad,
Did you call AddFeatureAsync? "Unable to complete operation" sounds like a server error. I was only able to reproduce this error on DeleteFeaturesAsync if I delete a feature that is not already on the table. It looks like delete operation can trigger an incomplete query in this case, since objectId attribute is not set - which is likely the bug - but would not have happened if feature was added to the table. Also a negative objectId when you have not called ApplyEditsAsync yet to submit your edits to the service.
So in the code below if you comment out AddFeatureAsync, you'll get "Unable to complete operation".
try
{
var location = await MyMapView.SketchEditor.StartAsync(SketchCreationMode.Point, false) as MapPoint;
var layer = (FeatureLayer)MyMapView.Map.OperationalLayers[0];
var table = (ArcGISFeatureTable)layer.FeatureTable;
var template = table.LayerInfo.FeatureTemplates.FirstOrDefault();
var feature = table.CreateFeature(template);
feature.Geometry = location;
await table.AddFeatureAsync(feature);
await layer.FeatureTable.DeleteFeatureAsync(feature);
}
catch (TaskCanceledException)
{
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
I don't think this is platform-specific but how are you storing the new feature?
You can use one of the following - GetAddedFeaturesAsync
var features = await ((ArcGISFeatureTable)layer.FeatureTable).GetAddedFeaturesAsync();
or through interaction from a GeoViewTapped event
var results = await MyMapView.IdentifyLayerAsync(layer, screenPoint, 1, false);
if (results.GeoElements != null)
{
foreach (var element in results.GeoElements)
{
await layer.FeatureTable.DeleteFeatureAsync((Feature)element);
}
}
Jennifer, thanks for the reply.
I am adding the feature similar to the code you show, but it is more like below since the user is adding the feature online, where apply edits is called.
try { var location = await MyMapView.SketchEditor.StartAsync(SketchCreationMode.Point, false) as MapPoint; var layer = (FeatureLayer)MyMapView.Map.OperationalLayers[0]; var table = (ArcGISFeatureTable)layer.FeatureTable; var template = table.LayerInfo.FeatureTemplates.FirstOrDefault(); var feature = table.CreateFeature(template); feature.Geometry = location; await table.AddFeatureAsync(feature); var serviceTable = table as ServiceFeatureTable; var results = await serviceTable.ApplyEditsAsync (); await layer.FeatureTable.DeleteFeatureAsync(feature); } catch (TaskCanceledException) { } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex.Message); }
In this code, you'll get the 'Unable to complete operation' error when DeleteFeaturesAsync() is called.
And, if you inspect the GlobalId, you'll see that it is set. But, if you use QueryFeaturesAsync or get the feature by tapping on it, you'll see that the GlobalId is different. Is this expected?
Is there a way to get/refresh the feature that has been added without going back to the server to get it (either by QueryFeaturesAsync or tapping the map)?
Should RetryLoadAsync() refresh the feature so it's what is on the server?
I get it if I need to go back to the server, but stumped on how to get the feature, other than use the attributes that the user entered since the ObjectId is a negative number (not refreshed from the server) and the GlobalId is different. Hope this makes sense. This is not an issue while offline, only when working in a connected environment.
Thanks again for your help.
Ah I see, since feature now exists on server after ApplyEditsAsync call, you can call feature.RefreshObjectId() before DeleteFeaturesAsync. DeleteFeatures failed for the same reason that it triggered a query on server with incomplete query. Note that Add/Update/DeleteFeaturesAsync or Add/Update/DeleteAttachmentsAsync are local changes until you call ApplyEditsAsync, at which time edits are sync'd to server. In the snippet above then feature is added to service but is not deleted until you call ApplyEditsAsync again.
Jennifer, thanks again, I'll try RefreshObjectId and see if that resolves my issue. I missed that one, sounds like it will fix my issue.
Thanks again, I'll post back my results after I get a chance to test.
I missed to address your other question about globalid and objectid. While a negative object id is expected for new features that only exists locally (before ApplyEdits), the global id and object id from server can be retrieved from ApplyEdits result. You can query for the updated feature using the following code as well.
var results = await table.ApplyEditsAsync();
if (results != null)
{
var query = new QueryParameters();
foreach (var r in results)
query.ObjectIds.Add(r.ObjectId);
var features = await table.QueryFeaturesAsync(query);
}
I think RefreshObjectId should have also updated the feature.Attributes - we'll try to get this fixed in future versions of the API. Adding RefreshObjectId before DeleteFeaturesAsync will delete the correct feature. But if you need the updated attributes, you can use QueryFeaturesAsync.
Jennifer, RefreshObjectId() doesn't seem to work, it stays a negative value. Should RetryLoadAsync() load the feature from the server?
I did confirm that getting the results from the EditResult returned from ApplyEditsAsync does in fact have the ObjectId and GlobalId values. Thanks for that tip, much appreciated.
Just curious, why does the GlobalId get set when the feature is added, but then changes after ApplyEditsAsync() is called?
await ftrTable.AddFeatureAsync (feature);
var gidFieldName = ((ArcGISFeatureTable)feature.FeatureTable).GlobalIdField;
object gid = string.Empty;
feature.Attributes.TryGetValue (gidFieldName, out gid);
var oidFieldName = ((ArcGISFeatureTable)feature.FeatureTable).ObjectIdField;
object oid = string.Empty;
feature.Attributes.TryGetValue (oidFieldName, out oid);
ToastHelpers.ShowInfo (oid.ToString (), gid.ToString (), 2);
feature.RefreshObjectId ();
oid = string.Empty;
feature.Attributes.TryGetValue (oidFieldName, out oid);
ToastHelpers.ShowInfo (oid.ToString (), gid.ToString (), 2);
ServiceFeatureTable serviceTable = ftrTable as ServiceFeatureTable;
IReadOnlyList<EditResult> results = await serviceTable.ApplyEditsAsync ();
foreach (EditResult result in results) {
ToastHelpers.ShowInfo (result.ObjectId.ToString (), result.GlobalId, 2);
}
Hi Chad,
RefreshObjectId() not updating attribute value is a bug which we'll have to fix. It remains to be the local cached values. To workaround this, you can query for the feature instead. However in my test, when RefreshObjectId is called after ApplyEdits, DeleteFeature does not fail. It deletes the correct feature, but attribute values does not reflect the same values from ApplyEdits result. Can you confirm?
Based on your feedback we also have to fix the issue where an incomplete query can occur if feature being deleted is either not part of the table or had been saved but not refreshed.
The GlobalId and ObjectId uniquely identifies the feature so they are both automatically generated as soon as feature is created and added to the table. When edits are submitted to the server, they are expected to change because say you have other clients pushing edits to the same server, each of those new features need to be given unique identifiers. ApplyEdits then would respond with the GlobalId and ObjectId assigned by the server.
Magical....it is not refreshing, but yeah, the delete does actually work. Thanks, better than going back to query for the feature again.
Thanks for the Id feedback. I understand the ObjectId, just thought the GlobalId would remain the same. Regardless, all set, thanks so much for helping out.
Just to add to this, I encountered the same issue when using AddFeatureAsync(), then ApplyEditsAsync(), and then without going back to the server to get the feature, calling UpdateFeatureAsync(). This actually throws a different error (I can't post if it will help, just let me know), but if you call RefreshObjectId on the feature, then everything works as expected.
Thanks again for your help in getting this figured out.