.Net Update Cursors SDE Editing and Updating Editor

671
7
11-10-2011 10:14 AM
RichardFairhurst
MVP Honored Contributor
Three questions:

1) I am trying to create an add-in for distribution that edits features both inside and outside of an edit session while Desktop is open.  If I was designing this just for my own use I would know the way the add-in behaved and would generally know not to try to edit a workspace I do not have write access to, but I do not want to assume that about my users.  What is the best way to test that a workspace can be edited by a user?

2) SDE seems to always need an Edit session started for any of the editing code I have ever gotten to work, but geoprocessing tools work on SDE data without an edit session being started by the user.  I have tried using the IWorkSpaceEdit to determine if an edit session already exists and if it does not I begin an edit session in the code and wrap the actual edits in a StartOperation and StopOperation pair.  I do not seem to see this behavior with non-SDE feature classes (which do not use a edit session and just update the feature directly).

Below is my code.  It completes fine, but the Editor in ArcMap does not actually start an Edit session and if after it completes I try to manually start an Edit Session on an affected SDE feature class it says it is not editable until I click on the map somewhere.  So it seems that somehow I am not releasing the lock on the feature class.  I am using an Update cursor, which I know does not fire events like the store method on a search cursor, and I notice that I cannot flush the cursor within the Edit Operation without triggering an error.  But I would like to use the Update Cursor for speed.  Any ideas on what I can do to get the edits to complete in a way that allows the user to begin an edit session? 

      ' Assume I have a valid Feature Class assigned to the pFC variable
   Try
      Dim pFCursor As ESRI.ArcGIS.Geodatabase.IFeatureCursor = Nothing
      pFCursor = pFC.Update(Nothing, False) ' Create an UpdateCursor on FeatureClass
      Dim pFeat As ESRI.ArcGIS.Geodatabase.IFeature
      pFeat = pFCursor.NextFeature ' Get First Feature
      If pFeat Is Nothing Then
        MsgBox("No Route Features Selected!")
        Return
      End If

      Dim pDataset As ESRI.ArcGIS.Geodatabase.IDataset = pFC
      Dim IsVersion As Boolean = False
      If TypeOf pDataset Is ESRI.ArcGIS.Geodatabase.IVersionedObject Then
        IsVersion = True
      End If
      Dim workspace As ESRI.ArcGIS.Geodatabase.IWorkspace = pDataset.Workspace
      ' use IWorkspaceEdit
      Dim pWorkspaceEdit As ESRI.ArcGIS.Geodatabase.IWorkspaceEdit = workspace


      If IsVersion Then
        If Not pWorkspaceEdit.IsBeingEdited Then
          pWorkspaceEdit.StartEditing(True)
          If Not pWorkspaceEdit.IsBeingEdited Then
            MsgBox("Cannot start editing SDE Workspace")
            Return
          Else
            pWorkspaceEdit.StartEditOperation()
          End If
        Else
          pWorkspaceEdit.StartEditOperation()
        End If
      End If

      Dim i as Long = 0 ' Initialize a feature counter

      Do While Not pFeat Is Nothing

        ' do some kind of edit of the feature here

        pFCursor.UpdateFeature(pFeat) ' Update the cursor
        pFeat = pFCursor.NextFeature
        i = i + 1 ' Counter for features processed
        If i Mod 1000 = 0 Then ' flush edits after every 1,000 features
          If Not IsVersion Then
            pFCursor.Flush()
          End If
        End If
      Loop

      If Not IsVersion Then
        pFCursor.Flush() ' Final Flush of edits
      Else
        pWorkspaceEdit.StopEditOperation()
      End If
   Catch ex As Exception
     MsgBox(ex.ToString)
   End Try


3)  If an Edit session is started on the workspace, I want to be able to update the Editor dialogs, such as the feature coordinate dialog that can be accessed when a single feature is selected and its vertices are being edited.  I know that the Update cursor won't do this as it is processing the  features.  Is there a way to get the Editor to see the updates when the edits are finished?  Do I have to use a SearchCursor and the feature's store method to make that happen?

Thanks for any help.
0 Kudos
7 Replies
RichardFairhurst
MVP Honored Contributor
After searching the forums a little more I came across a few help topics that seem to deal with many of my questions.  The most useful topics are Editing with the geodatabase API, Updating Features, Managing edit sessions and edit operations, IWorkspaceEdit Interface and IMultiuserWorkspaceEdit Interface

It looks like I will need to revise my code substantially to properly implement the recommended practices for editing with ArcObjects.  If I come up with a good workflow for properly checking the current state of the Editor relative to the layer I want to edit, determining appropriate editing options for a layer based on its data type, selecting the best method that enhances performance without compromising data, etc... I will post it here.
0 Kudos
RichardFairhurst
MVP Honored Contributor
A few observations in my testing of a new set of routines based on the help I found.  The help encourages the use of the Editor interfaces (IEditor, IEditor2 or IEditor3) for managing edit sessions rather than the Geodatabase interfaces (IWorkspaceEdit and IMultiuserWorkspaceEdit).  This appears to relate to the locking behavior I mentioned in my second question and to my third question about refreshing the Desktop editing tools like the Sketch Properties window.  The Editor interfaces are the only ones that affect what the user sees in Desktop.  The Geodatabases interfaces do not.

So if I use the Editor interfaces to start an edit session and an edit operation the Desktop responds by enabling the Editor menu and blanks out the Sketch Properties window.  When the edit operation complete it does not leave a lock that prevents user actions with editor in Desktop.  The operation can also be undone by the user through the normal undo interface if the edit session is left active and the edits are not saved.

But if I use the Geodatabase interfaces the Desktop does not respond at all and any failure to complete the edit session with that interface when the user had not started an edit session will interfere with the user trying to start an edit session in Desktop if the program edit session did not complete.  There also is no way for the user to undo the operation through the normal Desktop interface and only the program can manange any undo.  So to the extent that the Geodatabase interfaces are used the edit session those interfaces manage has to be completely self contained and must complete all operations and edit sessions begun by those interfaces in the code.

It appears to me that the Geodatabase interfaces are better suited to ArcCatalog operations rather than Desktop operations, given that these interfaces interfere with the Desktop edit session but do not give the user access through the normal interfaces to resolve any conflicts the program may have created and failed to resolve.  In Desktop the Geodatabase interfaces can be used to test for certain edit states that are not available with the Editor interfaces, but only the Editor interfaces should be used to actually start or stop the editor or the edit operations in Desktop code.
0 Kudos
RichardFairhurst
MVP Honored Contributor
On my first topic I have found several interfaces that need to be checked to determine if an Edit Session can be safely started on SDE data.  When dealing with SDE, you need to check if the feature class can be updated, deleted or inserted.  For that you can use the ISQLPrivilege interface:

      Dim workspace As ESRI.ArcGIS.Geodatabase.IWorkspace = pDataset.Workspace ' Assume pDataset is assigned a Dataset
      If workspace.Type = esriWorkspaceType.esriRemoteDatabaseWorkspace Then
        Dim pFCName As IFeatureClassName = pDataset.FullName
        Dim pSQLPriv As ISQLPrivilege = pFCName
        If pSQLPriv.SQLPrivileges And esriSQLPrivilege.esriUpdatePrivilege <> esriSQLPrivilege.esriUpdatePrivilege Then ' Bitwise check for Update Privileges
          MsgBox("You do not have priveleges to update this feature class!")
        End If
      End If

The Bitwise check can combine multiple states.  For example to see if all edit priveleges are granted use:

        If pSQLPriv.SQLPrivileges And (esriSQLPrivilege.esriUpdatePrivilege Or esriSQLPrivilege.esriDeletePrivilege Or esriSQLPrivilege.esriInsertPrivilege) <> (esriSQLPrivilege.esriUpdatePrivilege Or esriSQLPrivilege.esriDeletePrivilege Or esriSQLPrivilege.esriInsertPrivilege) Then ' Bitwise check for All Edit Privileges

Assuming the user has the approrpiate edit privileges you also need to check the current state of the Editor Options setting to permit versioned and non-versioned editing and compare it to the version status of the Dataset you want to edit.  The IEditProperties3 interface tells whether the Editor Options are set to allow versioned or non-versioned editing while the IVersionedObject3 can determine if an SDE dataset object is versioned or non-versioned.  The settings of the Editor and the Feature Class target must match to allow the Editor to be started:

Dim pVO3 As IVersionedObject3 = pDataset ' Assume a valid SDE Dataset is assigned to this variable
Dim pEP3 As IEditProperties3 = m_Editor ' assumes this variable is assigned with an instance of IEditor
Dim MESM As esriMultiuserEditSessionMode = pEP3.MultiuserEditSessionMode
If MESM = esriMultiuserEditSessionMode.esriMESMVersioned And Not pVO3.IsRegisteredAsVersioned Then
' Do something when the Edit Mode in set to versioned editing but the Dataset is non-versioned
ElseIf MESM = esriMultiuserEditSessionMode.esriMESMNonVersioned And pVO3.IsRegisteredAsVersioned Then
' Do something when the Edit Mode in set to non-versioned editing but the Dataset is versioned
End If

The MultiuserEditSessionMode setting can used to notify the user that there is a mismatch or it can actually change the Editor Option setting to match the dataset.  If the Editor Option is changed it should be reset back before the user can regain control or else the user should be allowed to determine if the tool should proceed and be notified of the change, since if the code does not change the setting back if will affect the user's ability to start or stop an edit session on versioned or non-versioned data.

I hope this helps someone.
0 Kudos
RichardFairhurst
MVP Honored Contributor
Here is another part of the answer to my first question.  To test workspaces if they are Read Only or if they can be edited use the IWorkspaceProperties and IWorkspaceProperty interfaces.  Below is a code sample of how to use these interfaces:

        ' Assume workspace is a variable containing a valid IWorkspace instance from a Layer.
        Dim workspaceProperties As IWorkspaceProperties = TryCast(workspace, IWorkspaceProperties)
        If workspaceProperties Is Nothing Then
          Throw New ArgumentException("Workspace does not implement IWorkspaceProperties.")
        End If
        Dim workspaceProperty As IWorkspaceProperty = workspaceProperties.Property _
                                                      (esriWorkspacePropertyGroupType.esriWorkspacePropertyGroup, _
                                                      CInt(esriWorkspacePropertyType.esriWorkspacePropIsReadonly))

        ' See if the property is supported.
        If workspaceProperty.IsSupported Then
          ' Get the property value.
          Dim bReadOnly As Boolean = CType(workspaceProperty.PropertyValue, Boolean)
          If bReadOnly Then
            MsgBox("The Workspace of this Layer is Read Only!" & vbCrLf & "You Cannot Edit This Layer!", MsgBoxStyle.Exclamation, "Read Only Workspace!")
            workspaceProperty = Nothing
            workspaceProperties = Nothing
            workspace = Nothing
            Exit Sub
          End If
        End If
        workspaceProperty = workspaceProperties.Property _
                                                      (esriWorkspacePropertyGroupType.esriWorkspacePropertyGroup, _
                                                      CInt(esriWorkspacePropertyType.esriWorkspacePropCanEdit))
        If workspaceProperty.IsSupported Then
          ' Get the property value.
          Dim bCanEdit As Boolean = CType(workspaceProperty.PropertyValue, Boolean)
          If Not bCanEdit Then
            MsgBox("The Workspace of this Layer Cannot be Edited!" & vbCrLf & "You Cannot Edit This Layer!", MsgBoxStyle.Exclamation, "Cannot Edit Workspace!")
            workspaceProperty = Nothing
            workspaceProperties = Nothing
            workspace = Nothing
            Exit Sub
          End If
        End If
0 Kudos
RichardFairhurst
MVP Honored Contributor
Unfortunately my last post did not work as I expected.  I tried it out on feature class in a network directory where I was not granted write permissions, just read, read and execute, and list directory permissions.  The code above returned that the workspace was not Read Only and that it Can Be Edited.  However, when it tried to start an edit session it threw an error as I expected.  I also looked at .Net code to directly check the directory permissions, but it is reporting the same thing.  But the attempt to start the editor in ArcMap manually brings up a report that the layer cannot be edited prior to starting the edit session.  So somehow the programmers for ArcMap have figured out what the real settings are, I just have not figured out what interfaces and methods they are using.

I have an incident open with Esri to try to get some help.  Any ideas from anyone who has done this out there?
0 Kudos
RichardFairhurst
MVP Honored Contributor
I was able to create the function I needed.  For non-SDE workspaces it tests the effective permission of the currentuser to see if they have security policy permissions to write to a directory workspace.

The other code for non-SDE workspaces I posted above that tests the read only and can edit properties also should be used, but that code only tests if the read only setting was applied directly to the individual file or directory.  That setting would prevent even the owner with full rights over the file or directory from being able to write to it without unchecking the flag.

The function below incorporates all of the tests I have mentioned and should work with any IDataset variable, whether it is assigned, unassigned. SDE or non-SDE.  If it is SDE it checks if it has Edit priveleges.  If it is non-SDE it checks user security policies which are applied based on the user identity and not uniformly to the file or directory.  It also checks the workspace rights mode settings of the file or directory itself as shown before.  I had to comment out the Threading interfaces and methods for checking the CurrentPrincipal because they threw errors, but I would have applied them if I could.  If anyone knows how to get them to work in an Add-in, please let me know.

Even if this code indicates that you can write to a dataset, you should still enclose any use of the Editor interface within a Try Catch block, since the file system is dynamic.  You should assume that it is possible for settings to change during your process in ways that may cause a failure based on the inability to write to the dataset even after this test is passed, but normally this test should be a good indicator that the dataset can be written to immediately after the test passes.  For SDE data you also still need to test if the Editor Option property for versioned/unversioned editing under IEditProperties3 matches the dataset's versioned state.  You also need to make sure that no edit session is already in progress before attempting to begin a new edit session using the IEditor interface.

  Public Function CanWriteTo(ByVal pDataset As IDataset) As Boolean
    If Not pDataset Is Nothing Then
      ' Dataset is valid
      Dim workspace As ESRI.ArcGIS.Geodatabase.IWorkspace = pDataset.Workspace
      If Not workspace Is Nothing Then
        ' Workspace is valid
        If workspace.Type = esriWorkspaceType.esriRemoteDatabaseWorkspace Then
          ' workspace is an SDE Worksapce.

          ' Verify editing privileges have been granted for the SDE data
          Dim pFCName As IFeatureClassName = pDataset.FullName
          Dim pSQLPriv As ISQLPrivilege = pFCName
          If (pSQLPriv.SQLPrivileges And (esriSQLPrivilege.esriSelectPrivilege Or esriSQLPrivilege.esriUpdatePrivilege Or esriSQLPrivilege.esriInsertPrivilege Or esriSQLPrivilege.esriDeletePrivilege)) = (esriSQLPrivilege.esriSelectPrivilege Or esriSQLPrivilege.esriUpdatePrivilege Or esriSQLPrivilege.esriInsertPrivilege Or esriSQLPrivilege.esriDeletePrivilege) Then
            pSQLPriv = Nothing
            pFCName = Nothing
            workspace = Nothing
            Return True
          End If
        Else
          ' workspace is Not an SDE Workspace
          Dim DirectoryPath As String = workspace.PathName
          Dim dirInfo As System.IO.DirectoryInfo = FileIO.FileSystem.GetDirectoryInfo(DirectoryPath)
          Dim currentUser As System.Security.Principal.WindowsIdentity = System.Security.Principal.WindowsIdentity.GetCurrent()
          ' Dim currentPrinciple As System.Security.Principal.WindowsPrincipal = System.Threading.Thread.CurrentPrincipal
          Dim acl As System.Security.AccessControl.AuthorizationRuleCollection = dirInfo.GetAccessControl().GetAccessRules(True, True, GetType(System.Security.Principal.SecurityIdentifier))
          Dim currentRule As System.Security.AccessControl.FileSystemAccessRule
          Dim bDenyWrite As Boolean = False
          Dim bAllowWrite As Boolean = False
          For x As Integer = 0 To acl.Count - 1
            currentRule = acl(x)
            If currentUser.User.Equals(currentRule.IdentityReference) Then ' Or currentPrinciple.IsInRole(currentRule.IdentityReference) Then
              If currentRule.AccessControlType.Equals(Security.AccessControl.AccessControlType.Deny) Then
                If (currentRule.FileSystemRights And Security.AccessControl.FileSystemRights.Write) = Security.AccessControl.FileSystemRights.Write Then bDenyWrite = True
                If (currentRule.FileSystemRights And Security.AccessControl.FileSystemRights.Read) = Security.AccessControl.FileSystemRights.Read Then bDenyWrite = True
              ElseIf (currentRule.FileSystemRights And (Security.AccessControl.FileSystemRights.Read Or Security.AccessControl.FileSystemRights.Write)) = (Security.AccessControl.FileSystemRights.Read Or Security.AccessControl.FileSystemRights.Write) Then
                bAllowWrite = True
              End If
            End If
          Next

          Dim workspaceProperties As IWorkspaceProperties = TryCast(workspace, IWorkspaceProperties)
          If workspaceProperties Is Nothing Then
            Throw New ArgumentException("Workspace does not implement IWorkspaceProperties.")
          End If
          Dim workspaceProperty As IWorkspaceProperty = workspaceProperties.Property _
                                                        (esriWorkspacePropertyGroupType.esriWorkspacePropertyGroup, _
                                                        CInt(esriWorkspacePropertyType.esriWorkspacePropIsReadonly))

          ' See if the property is supported.
          Dim bReadOnly As Boolean = False
          If workspaceProperty.IsSupported Then
            ' Get the property value.
            bReadOnly = CType(workspaceProperty.PropertyValue, Boolean)
          End If
          workspaceProperty = workspaceProperties.Property _
                                                        (esriWorkspacePropertyGroupType.esriWorkspacePropertyGroup, _
                                                        CInt(esriWorkspacePropertyType.esriWorkspacePropCanEdit))
          Dim bCanEdit As Boolean = False
          If workspaceProperty.IsSupported Then
            ' Get the property value.
            bCanEdit = CType(workspaceProperty.PropertyValue, Boolean)
          End If
          workspaceProperty = Nothing
          workspaceProperties = Nothing
          currentRule = Nothing
          currentUser = Nothing
          dirInfo = Nothing
          workspace = Nothing

          If bAllowWrite And Not (bDenyWrite) And Not (bReadOnly) And bCanEdit Then
            Return True
          End If
        End If
      End If
    End If
    Return False
  End Function


The function can be called using the code below:

        Dim bCanWrite As Boolean = CanWriteTo(pDataset) ' Assume pDataset is an IDataset variable.  It may be assigned or unassigned, SDE or non-SDE.
        If Not bCanWrite Then
          MsgBox("The Workspace Does Now Allow Write Access!", MsgBoxStyle.Exclamation, "No Write Access To Workspace!")
          Exit Sub ' one possible action for handling the inability to write to the dataset
        Else
          If Not pDataset Is Nothing Then 
            Dim workspace As ESRI.ArcGIS.Geodatabase.IWorkspace = pDataset.Workspaceaset
            If Not workspace Is Nothing Then
              If workspace.Type = esriWorkspaceType.esriRemoteDatabaseWorkspace Then
                Dim pVO3 As IVersionedObject3 = pDataset
                Dim pEP3 As IEditProperties3 = m_Editor ' assumes this variable is already assigned with an instance of IEditor
                Dim MESM As esriMultiuserEditSessionMode = pEP3.MultiuserEditSessionMode
                If MESM = esriMultiuserEditSessionMode.esriMESMVersioned And Not pVO3.IsRegisteredAsVersioned Then
                  ' Do something when the Edit Mode in set to versioned editing but the Dataset is non-versioned
                ElseIf MESM = esriMultiuserEditSessionMode.esriMESMNonVersioned And pVO3.IsRegisteredAsVersioned Then
                  ' Do something when the Edit Mode in set to non-versioned editing but the Dataset is versioned
                End If
              End If
              ' If IEditor is not already editing you may begin an IEditor edit session here if all the tests above passed and you resolved any mismatch in version edit mode
              ' You should still use a Try Catch block around your edit session.
            End If
          End If
        End If
0 Kudos
RichardFairhurst
MVP Honored Contributor
I found that the Bitwise check for SDE priveleges example I gave above needs to be fully enclosed in parentheses to work correctly.  So the correct syntax to check for all priveleges is:

If (pSQLPriv.SQLPrivileges And (esriSQLPrivilege.esriSelectPrivilege Or esriSQLPrivilege.esriUpdatePrivilege Or esriSQLPrivilege.esriDeletePrivilege Or esriSQLPrivilege.esriInsertPrivilege)) <> (esriSQLPrivilege.esriSelectPrivilege Or esriSQLPrivilege.esriUpdatePrivilege Or esriSQLPrivilege.esriDeletePrivilege Or esriSQLPrivilege.esriInsertPrivilege) Then ' Bitwise check for All Privileges
  ' Do something if user has all priveleges
End if

The code in the post just above this uses this syntax.
0 Kudos