Pro 2.2 issues?

1284
19
07-16-2018 12:15 PM
MKa
by
Occasional Contributor III

Has anyone had any issues after their upgrade to Pro 2.2.  It seems that most of my difference and merge functions are not performing as they did in 2.1?  I am wondering if anyone else is having this issue or I need to do something different when saving the result of that operation?

0 Kudos
19 Replies
MKa
by
Occasional Contributor III

So this is a drastic change then?  I cannot call apply changes from within a row create/row change event any longer?  Will the change you suggest work in 2.1?  Or do we have to make all these changes in 2.2 sdk and deploy to 2.2 installation.  My production code is out there in 2.1 and we are still developing against 2.1, so my question is will the code changes made to work in 2.2, still work in 2.1.  This will require some effort to get this upgraded.

My polygon features rely and other polygon features of a different class.  Some must be confined within a polygon of a different type, some cannot intersect a polygon of a different type.  Their attributes and shapes are changed accordingly using applyasync.  So imagine if I shrunk a "Parent" polygon, the child polygon of a different feature lies within that polygon.  When that parent polygon shrinks, the child polygon must shrink itself to stay confined, and in most cases update a number of area and location attributes.  

You are saying that I will need to update these values in a different way, whereas before I just told the underlying OID to basically apply changes and the row change event would take care of the work for that Object.  You are saying I can't call row change from withing a row change?

0 Kudos
Wolf
by Esri Regular Contributor
Esri Regular Contributor

Hi M,

 Sorry for the late reply, but I checked back with some of my colleagues to make sure that my findings were correct.  So to answer your question:  "So this is a drastic change then?"  According to the documentation "starting a new EditOperation within a Row Event" is not supported in 2.2 and this was also true in 2.1, so this is not a change in 2.2.  As to why this worked for you in 2.1 I can only speculate, but when I tried this workflow in my sample add-ins in 2.1 I was not able to get it to work reliably.  

 That being said let me answer your last question: "You are saying I can't call row change from within a row change?"  If you look at the sample ModifyNewlyAddedFeatures that I published it includes the "enable Store()" option which calls .Store() on the Row Event's subject row.  Calling Store() from within a Row Event can cause a Row Event to be triggered recursively and you can use the args.Guid parameter to determine re-entrance as illustrated by the sample.

 Finally to address your 2nd paragraph "My polygon features rely and other polygon features of a different class.I will modify my sample code so that creating or modifying a feature leads to changes in other features in order to emulate your requirements and post the updated sample.

0 Kudos
MKa
by
Occasional Contributor III

I still haven't gotten back to this, but what you mention above about using the args.Guid to determine reentrance is exactly what I do.  When we get to upgrading our version to 2.2 I will be able to work on this more.  But I am still suspecting that it has something to do with assigning the Geometry to an inspector on the Row when I load it from change event arg.  I think the Shape is not getting loaded correctly to my inspector and is somehow using the old shape on for the Row, and not the updated shape?

long _updateOID = args.Row.GetObjectID();

//Get the layer of the selected item
FeatureClass newFC = (FeatureClass)args.Row.GetTable();
string updateItemName = newFC.GetDefinition().GetAliasName();
FeatureLayer firstFeatureLayer = FeatureServiceManagement.GetFeatureLayer(updateItemName);

//Load the inspector
await inspr.LoadAsync(firstFeatureLayer, _updateOID);

.

0 Kudos
MKa
by
Occasional Contributor III

Above you say

"I think I saw your code snippets using an another EditOperation in one of the functions that are instantiated from the row events.  In 2.2 this will not work. "

This has to be my issue.  Within each create or update row event, my object tells itself to clip based on another shape of a certain type.  In that clip function is the difference command.  That difference command uses the edit operation modify and calls a commit operation to do the execution.  The commit operation keeps track of the OID so that the row change isn't fired again.  How do I go about switching from the edit operation code below, to committing my geometry changes for my inspector in the row events like you mention.  I need my geometry/shape change to be reflected.  But I don't see in your example how I can do this?

//Gets the Difference of this Item minus an input Geometry
public void Difference(Geometry inDifferenceGeometry)
{
   LogWriter.Log.Debug("   Item - Difference - Entered");

   try
   {
      //Get the difference of thisItem minus the inDifferenceGeometry
      if (!inDifferenceGeometry.IsNullOrEmpty())
      {
         Geometry originalShape = _dataInspector.Shape;
         Geometry differenceShape = GeometryEngine.Instance.Difference(originalShape, inDifferenceGeometry);

         _dataInspector.Shape = differenceShape;


         //create and execute the edit operation
         // create an edit operation
         EditOperation differenceOperation = new EditOperation(){
                    Name = "Difference Operation",
                    ProgressMessage = "Working...",
                    CancelMessage = "Operation canceled.",
                    ErrorMessage = "Error In Difference",
                    SelectModifiedFeatures = false,
                    SelectNewFeatures = false
               };

           differenceOperation.Modify(_dataInspector);

               
           CommitEditOperation(differenceOperation);
      }
  }
  catch (Exception e)
  {
    LogWriter.LogError("   Item - Difference", e);
  }
}

public void CommitEditOperation(EditOperation eo)
{
  long currentObjectID = OID;
  try
  { 
     PMModifiedBy = Settings.CurrentUserName;
     Module.CurrentlyUpdatingOIDList.Add(currentObjectID);
     LogWriter.Log.Debug("   Item - CommitEditOperation - Started " + eo.Name);
 
//AT THIS POINT MY SHAPE IS THE CORRECT NEW ARE, BUT THE Execute() CHANGES THAT back to the original shape

      bool operationResult = eo.Execute();
      LogWriter.Log.Debug("   Item - CommitEditOperation - Finished " + eo.Name);
  }
  catch (Exception e)
  {
    LogWriter.LogError("   Item - CommitEditOperation - " + eo.Name, e);
     throw;
  }
  finally
  {
    Module.CurrentlyUpdatingOIDList.Remove(currentObjectID);
  }
}
0 Kudos
MKa
by
Occasional Contributor III

I just attempted to remove the edit operation and just use apply on the inspector.  This also fails to update the geometry?  How do I update the geometry of the inspector?  If I catch the Arg in the Row Change Event, after I call Apply, the area value has reverted back to the pre difference value too?

//Gets the Difference of this Item minus an input Geometry
public void Difference(Geometry inDifferenceGeometry)
{
   LogWriter.Log.Debug("   Item - Difference - Entered");

   try
   {
      //Get the difference of thisItem minus the inDifferenceGeometry
      if (!inDifferenceGeometry.IsNullOrEmpty())
      {
         Geometry originalShape = _dataInspector.Shape;
         Geometry differenceShape = GeometryEngine.Instance.Difference(originalShape, inDifferenceGeometry);

         _dataInspector.Shape = differenceShape;

           if (_dataInspector != null)
            {
                long currentObjectID = OID;
                try
                {
                    ProMapBlackModule.CurrentlyUpdatingOIDList.Add(currentObjectID);
                    PMModifiedBy = Settings.CurrentUserName;

                         //AFTER THE APPLY, THE VALUE reverts back to the Original (Pre Difference Area???)
                    _dataInspector.Apply();
                }
                catch (Exception e)
                {
                    ProMapBlackLogWriter.LogError("ProMapItem - CommitChanges - " + MemberName, e);
                    throw;
                }
                finally
                {
                    ProMapBlackModule.CurrentlyUpdatingOIDList.Remove(currentObjectID);
                }
            }
      }
  }
  catch (Exception e)
  {
    LogWriter.LogError("   Item - Difference", e);
  }
}
0 Kudos
MKa
by
Occasional Contributor III

I have just confirmed that when I make changes to an inspector and call apply or applyasync, those changes do not come through with the "Args" on the OnRowChangeEvent.  Is there some other shape I need to change on the inspector I need to call before I call Apply()?

To test this, set up a Row Change on your module.  Then from another piece of code, update an inspectors shape to another shape (i am using a feature service).  The areas should be different at this point.  Note the Area of the shape at this point, then apply() that change. 

This will then trigger the row change event in the module.  In the change event, catch the Arg immediately and look at its shape area value, it should be the same as your inspector, but that shape has reverted back to the shape from before you made the original change. 

There is something going on here for sure?  The inspector shape is not updating or reverting somehow

0 Kudos
MKa
by
Occasional Contributor III

I think I have a work around for this issue, but don't understand why I have to do this.  The problem with this, is that when .Apply is done to an Inspector that has its shape changed, that change is not happening or saved.  Is anyone else experiencing this?  Apply and Apply async don't update my inspector shape in the database, it rolls the change back.  All other attributes appear to update, but shape rolls back.

My workaround is to create a static geometry variable in my module.  This Geometry variable needs to be set to a geometry value that you want to change for an inspector somewhere.  That way, when on the OnChange event comes in, the arg can set its shape to this value.  This is the only way I can get this to work.  This has to be a bug of some kind

I used to be able to set my geometry for an inspector and call Apply Async

_dataInspector.Shape = NewBufferShape;
await _dataInspector.ApplyAsync();

in 2.2 the above won't work any longer, as the Apply on the inspector doesn't appear to save the shape anymore.  You can test this by putting breakpoint in the OnChange event of the module and seeing that the area on the arg is not the same as the area on the inspector.  You can also change the shape of the inspector, note the shape area, apply changes and check the shape area again right after apply is done.  It will have rolled back. 

So the work around has to be to set up a static variable to set on the module to force the Arg to set the new geometry

//Layer object code on how to update
Module.CurrentlyUpdatingGeometry = _dataInspector.Shape;
await _dataInspector.ApplyAsync();
Module.CurrentlyUpdatingGeometry = null;


//Module Code
public static Geometry CurrentlyUpdatingGeometry;
protected static async void OnRowChangeEvent(RowChangedEventArgs args)
{           
   if (!CurrentlyUpdatingGeometry.IsNullOrEmpty())
   {
      args.Row["Shape"] = CurrentlyUpdatingGeometry;
   }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
.
.
.
.
.‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
}
0 Kudos
CharlesMacleod
Esri Regular Contributor

Using the row, passed as the argument to the row event, as you illustrate above is the correct pattern. Use of the inspector to apply an edit in any of the row events should not be attempted (whether it worked previously or not).

 
 private void RowEvent(....

 //this is correct
 args.Row["Shape"] = CurrentlyUpdatingGeometry;
 args.Row.Store();

 //this is not. It triggers another edit operation
 insp["Shape"] = CurrentlyUpdatingGeometry;
 insp.Apply(); //<-- attempts to execute another edit operation

Note that row events are fired during the store* on the underlying geodatabase - in other words, before the store has completed but after it has started. Therefore, any changes you are applying in an edit operation (that has triggered a row event) have not yet been committed and are in flight. This is why, in most cases, they are cancel-able regardless of the transaction type (short or long). It is also why, if you re-access the row via the layer or the feature class, "in flight", you get the "original" value.

*Non-versioned feature services on a create fire the row event after the store on the server.

Below is a code example you can play with to test this behavior. I am running it against an editable hosted point feature service:   The code creates a feature and then moves it to another location. The feature shape is output at various instances within the transaction - Before, During ("in flight"), and After. This is the output:

Create location: 60546084,2112494 <-- start location
Create oid: 44

Intended Move location: 60546634,2113044  <-- what we pass to modify
INFLIGHT: Move location: 60546634.0001205,2113043.99987471 <-- from Row["SHAPE"] in event
DATABASE: Move location: 60546083.9999188,2112494.00000116 <-- from GDB in event
Final Move location: 60546634.0001205,2113043.99987471  <-- final after event (and commit)
‍‍‍‍‍‍‍

Notice that the "move" location in the event ("INFLIGHT") within the "modify-ing" row ("args.Row" or "rc.Row" in my example), is the intended final location. Notice that the location I retrieve from the database in the event is not. It is still the "pre" location as the database has not yet been modified and will not be modified until after the execute() has concluded successfully. After execute(), re accessing the database gets the (correct) final location.

 internal class TestRowEvent : Button
  {
    private SubscriptionToken _token = null;
    private double _x = 60546084.0;
    private double _y = 2112494.0;
    private double _delta = 550.0;

    protected async override void OnClick()
    {
      await QueuedTask.Run(() => {
        var fs_layer = MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>()
          .First(fl => fl.Name == "<name of layer here>");

        var editOp = new EditOperation();
        editOp.Name = $"Create {fs_layer.Name}";
        editOp.SelectNewFeatures = true;
        //make the locations
        var pt = MapPointBuilder.CreateMapPoint(_x, _y, fs_layer.GetSpatialReference());
        Dictionary<string, object> attributes = new Dictionary<string, object>();
        attributes["SHAPE"] = pt;
        attributes["...."] = "value 1";
        //etc

        long _newoid = -1;
        editOp.Create(fs_layer, attributes, (oid) => _newoid = oid);
        editOp.Execute();
        editOp = null;

        System.Diagnostics.Debug.WriteLine($"Create location: {_x},{_y}");
        System.Diagnostics.Debug.WriteLine($"Create oid: {_newoid}");

        //move the feature
        pt = MapPointBuilder.CreateMapPoint(_x + _delta, _y + _delta, fs_layer.GetSpatialReference());

        editOp = new EditOperation();
        editOp.Name = $"Move {fs_layer.Name}";
        editOp.SelectModifiedFeatures = true;
        editOp.Modify(fs_layer, _newoid, pt);
        System.Diagnostics.Debug.WriteLine($"Intended Move location: {_x + _delta},{_y + _delta}");

        //show the geometry "in-flight"...
        _token = ArcGIS.Desktop.Editing.Events.RowChangedEvent.Subscribe((rc) =>
        {
          var inflight_pt = rc.Row["SHAPE"] as MapPoint;
          System.Diagnostics.Debug.WriteLine($"INFLIGHT: Move location: {inflight_pt.X},{inflight_pt.Y}");
          //pull location from the database
          using (var rowcursor = fs_layer.GetSelection().Search())
          {
            rowcursor.MoveNext();
            var db_pt = rowcursor.Current["SHAPE"] as MapPoint;
            System.Diagnostics.Debug.WriteLine($"DATABASE: Move location: {db_pt.X},{db_pt.Y}");
          }
          ArcGIS.Desktop.Editing.Events.RowChangedEvent.Unsubscribe(_token);
          _token = null;

        }, fs_layer.GetTable());

        editOp.Execute();

        //get the modified feature
        var insp = new Inspector();
        insp.Load(fs_layer, _newoid);
        var move_pt = insp["SHAPE"] as MapPoint;
        System.Diagnostics.Debug.WriteLine($"Final Move location: {move_pt.X},{move_pt.Y}");

      });
    }
  }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos
MKa
by
Occasional Contributor III

So i should be have been using a rowcursor this whole time and not the inspector.  Much like we use to do in arcobjects?  I found the inspector very easy to use, and had to use the OID to prevent reentry into the change event, but other than that it worked great.  This is triggered when a user creates a feature.  Basically I send the inspr to my factory and it does all the work.  So I should remove the inspector and switch it to the row?   And use store instead of the apply methods on Inspector?

protected static async void OnRowCreateEvent(RowChangedEventArgs args)
        {
            try
            {
                Inspector inspr = await GetInspectorForRow(args, true);
                PMItem pmi = PMFactory.GetPMItem(inspr);

                if (pmi != null && Settings.CurrentMapSettings != null)
                {                    
                    pmi.PlantCode = Settings.CurrentMapSettings.PlantCode;
                    pmi.HarvestYear = Settings.CurrentMapSettings.HarvestYear;
                    pmi.HarvestTrimester = Settings.CurrentMapSettings.HarvestTrimester;
                    pmi.MaterialGroup = Settings.CurrentMapSettings.MaterialGroup;

                    //Override of Created/Modified
                    pmi.PMCreatedBy = Settings.CurrentUserName;
                    pmi.PMModifiedBy = Settings.CurrentUserName;

                    //Make the initial commit to get the field to be visible with the definition query
                    await pmi.CommitChangesAsync();

                    pmi.ShowEditForm = true;
                    pmi.HasShapeChanged = true;
                    pmi.IsNew = true;
                    await pmi.Edit();
                }
            }
            catch (Exception e)
            {
                LogWriter.LogError("PMBlackModule - onRowCreateEvent", e);
            }
        }
0 Kudos
CharlesMacleod
Esri Regular Contributor

Correct. Within the row events you must use Row.Store to apply changes. You will still have to deal with re-entrancy caused by Store.

0 Kudos