How to merge conflicting geometries during a reconcile


Summary Programmatically, it is possible to create a geometry object based on the merged shapes of two geometries. An interesting application of this functionality is in a multiuser environment where there is a potential of multiple representations of the same feature. This topic discusses how to use the merge geometry functionality in the context of a conflict on the shape attribute between versions.

In this topic


Implementing merge geometries within a version event

If the same feature is modified in two different versions, the shape attribute for that feature will always be in conflict. This makes logical sense because the binary shape object will have changed on each version. In the event of a reconcile between the two versions, the geometry change can be resolved in favor of either version, but this means that the shape changes on one of the versions will be lost. Even though these types of conflicts are valid, for many workflows it is not always desirable to have UpdateUpdate conflicts on the shape attribute.
For example, take the scenario of two editors editing in their own version to modify the same coastline polygon feature. If the first editor modified a section of coastline and another editor modified the same feature in a completely spatially unrelated section of the coastline, both changes are technically valid. However, it is possible to merge conflicting geometries when a conflict arises on the shape field. Merging both geometries attempts to create one geometry, which is the result of a merge between two other geometries. The merging geometries functionality can be leveraged through the use of the IConstructMerge interface in the Geometry library.

Listening to the versioned event

Do the following to listen to the versioned event:
  1. If the goal is to merge any conflicting edits to the shape field, subscribe to the OnConflictsDetected event.
  2. This event is raised when conflicts are detected on a reconcile between versions and allows the application developer to make changes to the version based on the results of the reconcile. For more information on listening to versioned events, see Listening to versioned events.

    See the following code example—the constructor of a custom listener class—that subscribes to the OnConflictsDetected event:
[C#]
public MergeEventListener(IVersion version)
{

    // Save the version as a member variable.
    featureWorkspace = (IFeatureWorkspace)version;

    // Subscribe to the OnReconcile event.
    IVersionEvents_Event versionEvent = (IVersionEvents_Event)version;
    versionEvent.OnConflictsDetected += new
        IVersionEvents_OnConflictsDetectedEventHandler(OnConflictsDetected);
}

public void OnConflictsDetected(ref bool conflictsRemoved, ref bool errorOccurred,
    ref string errorString)
{
    // TODO: Implement custom behavior.
}
[VB.NET]
Public Sub New(ByVal Version As IVersion)
    ' Save the version as a member variable.
    featureWorkspace = CType(Version, IFeatureWorkspace)
    
    ' Subscribe to the OnReconcile event.
    Dim versionEvent As IVersionEvents_Event = CType(Version, IVersionEvents_Event)
    AddHandler versionEvent.OnConflictsDetected, AddressOf OnConflictsDetected
End Sub

Public Sub OnConflictsDetected(ByRef conflictsRemoved As Boolean, ByRef errorOccurred As Boolean, _
                               ByRef errorString As String)
    ' TODO: Implement custom behavior.
End Sub

Getting the classes in conflict

Do the following to get the classes in conflict:
  1. After a handler has subscribed to the OnConflictsDetected event, it is possible to implement custom code to execute when conflicts are detected. In this case, the IVersionEdit.ConflictClasses method is leveraged to get an enumeration of all the classes with conflicts.
  2. Looping through each conflicting class, request the IConflictClass interface for a selection set of all the UpdateUpdate conflicts found on the class.

    See the following code example:
[C#]
IVersionEdit4 versionEdit4 = (IVersionEdit4)featureWorkspace;

// Get the various versions on which to output information.
IFeatureWorkspace commonAncestorFWorkspace = (IFeatureWorkspace)
    versionEdit4.CommonAncestorVersion;
IFeatureWorkspace preReconcileFWorkspace = (IFeatureWorkspace)
    versionEdit4.PreReconcileVersion;
IFeatureWorkspace reconcileFWorkspace = (IFeatureWorkspace)
    versionEdit4.ReconcileVersion;

IEnumConflictClass enumConflictClass = versionEdit4.ConflictClasses;
IConflictClass conflictClass = null;
while ((conflictClass = enumConflictClass.Next()) != null)
{
    IDataset dataset = (IDataset)conflictClass;

    // Make sure the class is a feature class.
    if (dataset.Type == esriDatasetType.esriDTFeatureClass)
    {
        String datasetName = dataset.Name;
        IFeatureClass featureClass = featureWorkspace.OpenFeatureClass(datasetName);

        Console.WriteLine("Conflicts on feature class {0}", datasetName);

        // Get all UpdateUpdate conflicts.
        ISelectionSet updateUpdates = conflictClass.UpdateUpdates;
[VB.NET]
Dim versionEdit4 As IVersionEdit4 = CType(featureWorkspace, IVersionEdit4)

' Get the various versions on which to output information.
Dim commonAncestorFWorkspace As IFeatureWorkspace = CType(versionEdit4.CommonAncestorVersion, IFeatureWorkspace)
Dim preReconcileFWorkspace As IFeatureWorkspace = CType(versionEdit4.PreReconcileVersion, IFeatureWorkspace)
Dim reconcileFWorkspace As IFeatureWorkspace = CType(versionEdit4.ReconcileVersion, IFeatureWorkspace)

Dim enumConflictClass As IEnumConflictClass = versionEdit4.ConflictClasses
Dim conflictClass As IConflictClass = enumConflictClass.Next()
Do While Not conflictClass Is Nothing
    Dim dataset As IDataset = CType(conflictClass, IDataset)
    
    ' Make sure the class is a feature class.
    If (dataset.Type = esriDatasetType.esriDTFeatureClass) Then
        Dim datasetName As String = dataset.Name
        Dim featureClass As IFeatureClass = featureWorkspace.OpenFeatureClass(datasetName)
        
        Console.WriteLine("Conflicts on feature class {0}", datasetName)
        
        ' Get all UpdateUpdate conflicts.
        Dim updateUpdates As ISelectionSet = conflictClass.UpdateUpdates

Determining if the shape is in conflict

Do the following to determine if the shape is in conflict:
  1. For every conflicting feature on a conflicting class, it is necessary to determine if the Shape column is in conflict. The IConflictClass interface returns references to the different versions used during a reconcile.

    See the following code example that gets a reference to the conflict feature on each of the reconcile versions:
[C#]
if (updateUpdates.Count > 0)
{
    // Get conflict feature classes on the three reconcile versions.
    IFeatureClass featureClassPreReconcile = preReconcileFWorkspace.OpenFeatureClass
        (datasetName);
    IFeatureClass featureClassReconcile = reconcileFWorkspace.OpenFeatureClass
        (datasetName);
    IFeatureClass featureClassCommonAncestor =
        commonAncestorFWorkspace.OpenFeatureClass(datasetName);

    // Iterate through each OID, outputting information.
    IEnumIDs enumIDs = updateUpdates.IDs;
    int oid =  - 1;
    while ((oid = enumIDs.Next()) !=  - 1)
    //Loop through all conflicting features. 
    {
        Console.WriteLine("UpdateUpdate conflicts on feature {0}", oid);

        // Get conflict feature on the three reconcile versions.
        IFeature featurePreReconcile = featureClassPreReconcile.GetFeature(oid);
        IFeature featureReconcile = featureClassReconcile.GetFeature(oid);
        IFeature featureCommonAncestor = featureClassCommonAncestor.GetFeature(oid);
[VB.NET]
If (updateUpdates.Count > 0) Then
    ' Get conflict feature classes on the three reconcile versions.
    Dim featureClassPreReconcile As IFeatureClass = preReconcileFWorkspace.OpenFeatureClass(datasetName)
    Dim featureClassReconcile As IFeatureClass = reconcileFWorkspace.OpenFeatureClass(datasetName)
    Dim featureClassCommonAncestor As IFeatureClass = commonAncestorFWorkspace.OpenFeatureClass(datasetName)
    
    ' Iterate through each OID, outputting information.
    Dim enumIDs As IEnumIDs = updateUpdates.IDs
    Dim oid As Integer = enumIDs.Next()
    Do While Not oid = -1 ' Loop through all conflicting features.
        Console.WriteLine("UpdateUpdate conflicts on feature {0}", oid)
        
        ' Get conflict feature on the three reconcile versions.
        Dim featurePreReconcile As IFeature = featureClassPreReconcile.GetFeature(oid)
        Dim featureReconcile As IFeature = featureClassReconcile.GetFeature(oid)
        Dim featureCommonAncestor As IFeature = featureClassCommonAncestor.GetFeature(oid)
  1. Comparing the shape attribute on these different versions is necessary to determine if the Shape column is in conflict. When comparing shape attributes, use the IClone interface. Since the IGeometry interface supports IClone, the IClone.IsEqual method is a quick and simple way of determining equivalency between two different geometries.

    See the following code example:
[C#]
// Method to determine if the shape field is in conflict.
private bool IsShapeInConflict(IFeature commonAncestorFeature, IFeature
    preReconcileFeature, IFeature reconcileFeature)
{
    // 1st check: Common Ancestor with PreReconcile.
    // 2nd check: Common Ancestor with Reconcile. 
    // 3rd check: Reconcile with PreReconcile (case of same change on both versions).
    if (IsGeometryEqual(commonAncestorFeature.ShapeCopy,
        preReconcileFeature.ShapeCopy) || IsGeometryEqual
        (commonAncestorFeature.ShapeCopy, reconcileFeature.ShapeCopy) ||
        IsGeometryEqual(reconcileFeature.ShapeCopy, preReconcileFeature.ShapeCopy))
    {
        return false;
    }

    else
    {
        return true;
    }
}

// Method returning if two shapes are equal to one another.
private bool IsGeometryEqual(IGeometry shape1, IGeometry shape2)
{
    if (shape1 == null & shape2 == null)
    {
        return true;
    }
    else if (shape1 == null ^ shape2 == null)
    {
        return false;
    }
    else
    {
        IClone clone1 = (IClone)shape1;
        IClone clone2 = (IClone)shape2;
        return clone1.IsEqual(clone2);
    }
}
[VB.NET]
' Method to determine if the shape field is in conflict.

Private Function IsShapeInConflict(ByVal commonAncestorFeature As IFeature, ByVal preReconcileFeature As IFeature, _
                                   ByVal reconcileFeature As IFeature) As Boolean
    ' 1st check: Common Ancestor with PreReconcile.
    ' 2nd check: Common Ancestor with Reconcile.
    ' 3rd check: Reconcile with PreReconcile (case of same change on both versions).
    If IsGeometryEqual(commonAncestorFeature.ShapeCopy, preReconcileFeature.ShapeCopy) Or _
                          IsGeometryEqual(commonAncestorFeature.ShapeCopy, reconcileFeature.ShapeCopy) Or _
                          IsGeometryEqual(reconcileFeature.ShapeCopy, preReconcileFeature.ShapeCopy) Then
        Return False
    Else
        Return True
    End If
End Function

' Method returning if two shapes are equal to one another.

Private Function IsGeometryEqual(ByVal shape1 As IGeometry, ByVal shape2 As IGeometry) As Boolean
    If shape1 Is Nothing And shape2 Is Nothing Then
        Return True
    ElseIf shape1 Is Nothing Xor shape2 Is Nothing Then
        Return False
    Else
        Dim clone1 As IClone = CType(shape1, IClone)
        Dim clone2 As IClone = CType(shape2, IClone)
        Return clone1.IsEqual(clone2)
    End If
End Function

Merging the conflicting geometries

Do the following to merge the conflicting geometries:
  1. When the geometries are in conflict, an attempt can be made to merge the geometries using the IConstructMerge interface. There are scenarios where the merge fails; therefore, handling any errors that MergeGeometries can produce is necessary.
  2. On a successful merge, set the resulting geometry to the current shape object and the result is a new shape incorporating the shape changes on both versions.

    See the following code example:
[C#]
// Check to make sure each shape is different than the common ancestor (conflict is on shape field).
if (IsShapeInConflict(featureCommonAncestor, featurePreReconcile, featureReconcile))
{
    Console.WriteLine(" Shape attribute has changed on both versions...");

    // Geometries are in conflict ... merge geometries.
    try
    {
        IConstructMerge constructMerge = new GeometryEnvironmentClass();
        IGeometry newGeometry = constructMerge.MergeGeometries
            (featureCommonAncestor.ShapeCopy, featureReconcile.ShapeCopy,
            featurePreReconcile.ShapeCopy);

        //Setting new geometry as a merge between the two versions.
        IFeature feature = featureClass.GetFeature(oid);
        feature.Shape = newGeometry;
        feature.Store();
        updateUpdates.RemoveList(1, ref oid);
        conflictsRemoved = true;
    }
    catch (COMException comExc)
    {
        // Check if the error is from overlapping edits.
        if (comExc.ErrorCode == (int)
            esriGeometryError.E_GEOMETRY_EDITED_REGIONS_OVERLAP)
        {
            // Edited areas overlap.
            Console.WriteLine("Error from overlapping edits on feature {0}", oid);
            Console.WriteLine(" Error Message: {0}", comExc.Message);
            Console.WriteLine(" Can't merge overlapping edits to same feature.");
        }
        else
        {
            // Unexpected COM exception, throw this to the exception handler.
            throw comExc;
        }
    }
}

else
{
    Console.WriteLine(" Shape field not in conflict: merge not necessary ... ");
}
[VB.NET]
' Check to make sure each shape is different than the common ancestor (conflict is on shape field).
If IsShapeInConflict(featureCommonAncestor, featurePreReconcile, featureReconcile) Then
    
    Console.WriteLine(" Shape attribute has changed on both versions...")
    
    ' Geometries are in conflict ... merge geometries.
    Try
    Dim constructMerge As IConstructMerge = New GeometryEnvironmentClass()
    Dim newGeometry As IGeometry = constructMerge.MergeGeometries(featureCommonAncestor.ShapeCopy, featureReconcile.ShapeCopy, featurePreReconcile.ShapeCopy)
    
    ' Setting new geometry as a merge between the two versions.
    Dim feature As IFeature = featureClass.GetFeature(oid)
    feature.Shape = newGeometry
    feature.Store()
    updateUpdates.RemoveList(1, oid)
    conflictsRemoved = True
    Catch comExc As COMException
    ' Check if the error is from overlapping edits.
    If comExc.ErrorCode = fdoError.FDO_E_WORKSPACE_EXTENSION_DATASET_CREATE_FAILED Or _
                          comExc.ErrorCode = fdoError.FDO_E_WORKSPACE_EXTENSION_DATASET_DELETE_FAILED Then
        ' Edited areas overlap.
        Console.WriteLine("Error from overlapping edits on feature {0}", oid)
        Console.WriteLine(" Error Message: {0}", comExc.Message)
        Console.WriteLine(" Can't merge overlapping edits to same feature.")
    Else
        ' Unexpected COM exception, throw this to the exception handler.
        Throw comExc
    End If
    End Try
Else
    Console.WriteLine(" Shape field not in conflict: merge not necessary ... ")
End If

Removing the feature from the conflict set

Do the following to remove the conflict from the conflict class:
  1. Call the Remove method to remove the specific feature from the UpdateUpdate conflict set.
  2. When removing objects from the conflict set, set the conflictsRemoved property on the event to true so the conflicts sets are consistent with the changes made within the event.

    See the following code example:
[C#]
updateUpdates.RemoveList(1, ref oid);
conflictsRemoved = true;
[VB.NET]
updateUpdates.RemoveList(1, oid)
conflictsRemoved = True

Complete code example

By iterating through the ObjectIDs from each conflict class, the code attempts to merge any conflicting geometries, and removes the feature from the UpdateUpdate conflict set if successful.
See the following complete code example:
[C#]
public class MergeEventListener
{
    private IFeatureWorkspace featureWorkspace = null;

    public MergeEventListener(IVersion version)
    {
        // Save the version as a member variable.
        featureWorkspace = (IFeatureWorkspace)version;

        // Subscribe to the OnReconcile event.
        IVersionEvents_Event versionEvent = (IVersionEvents_Event)version;
        versionEvent.OnConflictsDetected += new
            IVersionEvents_OnConflictsDetectedEventHandler(OnConflictsDetected);
    }

    /// <summary>
    /// Pseudocode:
    /// - Loop through all conflict classes after the reconcile.
    /// - Loop through every UpdateUpdate conflict on the class.
    /// - Determine if geometry is in conflict on the feature.
    /// - If so, merge geometries together (handling errors) and store the feature.
    /// </summary>
    public void OnConflictsDetected(ref bool conflictsRemoved, ref bool
        errorOccurred, ref string errorString)
    {
        try
        {
            IVersionEdit4 versionEdit4 = (IVersionEdit4)featureWorkspace;

            // Get the various versions on which to output information.
            IFeatureWorkspace commonAncestorFWorkspace = (IFeatureWorkspace)
                versionEdit4.CommonAncestorVersion;
            IFeatureWorkspace preReconcileFWorkspace = (IFeatureWorkspace)
                versionEdit4.PreReconcileVersion;
            IFeatureWorkspace reconcileFWorkspace = (IFeatureWorkspace)
                versionEdit4.ReconcileVersion;

            IEnumConflictClass enumConflictClass = versionEdit4.ConflictClasses;
            IConflictClass conflictClass = null;
            while ((conflictClass = enumConflictClass.Next()) != null)
            {
                IDataset dataset = (IDataset)conflictClass;

                // Make sure the class is a feature class.
                if (dataset.Type == esriDatasetType.esriDTFeatureClass)
                {
                    String datasetName = dataset.Name;
                    IFeatureClass featureClass = featureWorkspace.OpenFeatureClass
                        (datasetName);

                    Console.WriteLine("Conflicts on feature class {0}", datasetName);

                    // Get all UpdateUpdate conflicts.
                    ISelectionSet updateUpdates = conflictClass.UpdateUpdates;
                    if (updateUpdates.Count > 0)
                    {
                        // Get conflict feature classes on the three reconcile versions.
                        IFeatureClass featureClassPreReconcile =
                            preReconcileFWorkspace.OpenFeatureClass(datasetName);
                        IFeatureClass featureClassReconcile =
                            reconcileFWorkspace.OpenFeatureClass(datasetName);
                        IFeatureClass featureClassCommonAncestor =
                            commonAncestorFWorkspace.OpenFeatureClass(datasetName);

                        // Iterate through each OID, outputting information.
                        IEnumIDs enumIDs = updateUpdates.IDs;
                        int oid =  - 1;
                        while ((oid = enumIDs.Next()) !=  - 1)
                        //Loop through all conflicting features. 
                        {
                            Console.WriteLine(
                                "UpdateUpdate conflicts on feature {0}", oid);

                            // Get conflict feature on the three reconcile versions.
                            IFeature featurePreReconcile =
                                featureClassPreReconcile.GetFeature(oid);
                            IFeature featureReconcile =
                                featureClassReconcile.GetFeature(oid);
                            IFeature featureCommonAncestor =
                                featureClassCommonAncestor.GetFeature(oid);

                            // Check to make sure each shape is different than the common ancestor (conflict is on shape field).
                            if (IsShapeInConflict(featureCommonAncestor,
                                featurePreReconcile, featureReconcile))
                            {
                                Console.WriteLine(
                                    " Shape attribute has changed on both versions...");

                                // Geometries are in conflict ... merge geometries.
                                try
                                {
                                    IConstructMerge constructMerge = new
                                        GeometryEnvironmentClass();
                                    IGeometry newGeometry =
                                        constructMerge.MergeGeometries
                                        (featureCommonAncestor.ShapeCopy,
                                        featureReconcile.ShapeCopy,
                                        featurePreReconcile.ShapeCopy);

                                    // Setting new geometry as a merge between the two versions.
                                    IFeature feature = featureClass.GetFeature(oid);
                                    feature.Shape = newGeometry;
                                    feature.Store();
                                    updateUpdates.RemoveList(1, ref oid);
                                    conflictsRemoved = true;
                                }
                                catch (COMException comExc)
                                {
                                    // Check if the error is from overlapping edits.
                                    if (comExc.ErrorCode == (int)
                                        fdoError.FDO_E_WORKSPACE_EXTENSION_DATASET_CREATE_FAILED || comExc.ErrorCode == (int)fdoError.FDO_E_WORKSPACE_EXTENSION_DATASET_DELETE_FAILED)
                                    {
                                        // Edited areas overlap.
                                        Console.WriteLine(
                                            "Error from overlapping edits on feature {0}", oid);
                                        Console.WriteLine(" Error Message: {0}",
                                            comExc.Message);
                                        Console.WriteLine(
                                            " Can't merge overlapping edits to same feature.");
                                    }
                                    else
                                    {
                                        // Unexpected COM exception, throw this to the exception handler.
                                        throw comExc;
                                    }
                                }
                            }
                            else
                            {
                                Console.WriteLine(
                                    " Shape field not in conflict: merge not necessary ... ");
                            }
                        }
                    }
                }
            }
        }
        catch (COMException comExc)
        {
            Console.WriteLine("Error Message: {0}, Error Code: {1}", comExc.Message,
                comExc.ErrorCode);
        }
        catch (Exception exc)
        {
            Console.WriteLine("Unhandled Exception: {0}", exc.Message);
        }
    }

    // Method to determine if the shape field is in conflict.
    private bool IsShapeInConflict(IFeature commonAncestorFeature, IFeature
        preReconcileFeature, IFeature reconcileFeature)
    {
        // 1st check: Common Ancestor with PreReconcile.
        // 2nd check: Common Ancestor with Reconcile. 
        // 3rd check: Reconcile with PreReconcile (case of same change on both versions).
        if (IsGeometryEqual(commonAncestorFeature.ShapeCopy,
            preReconcileFeature.ShapeCopy) || IsGeometryEqual
            (commonAncestorFeature.ShapeCopy, reconcileFeature.ShapeCopy) ||
            IsGeometryEqual(reconcileFeature.ShapeCopy,
            preReconcileFeature.ShapeCopy))
        {
            return false;
        }
        else
        {
            return true;
        }
    }

    // Method returning if two shapes are equal to one another.
    private bool IsGeometryEqual(IGeometry shape1, IGeometry shape2)
    {
        if (shape1 == null & shape2 == null)
        {
            return true;
        }
        else if (shape1 == null ^ shape2 == null)
        {
            return false;
        }
        else
        {
            IClone clone1 = (IClone)shape1;
            IClone clone2 = (IClone)shape2;
            return clone1.IsEqual(clone2);
        }
    }
}
[VB.NET]
Public Class MergeEventListener
    
    Dim featureWorkspace As IFeatureWorkspace = Nothing
    
    Public Sub New(ByVal Version As IVersion)
        ' Save the version as a member variable.
        featureWorkspace = CType(Version, IFeatureWorkspace)
        
        ' Subscribe to the OnReconcile event.
        Dim versionEvent As IVersionEvents_Event = CType(Version, IVersionEvents_Event)
        AddHandler versionEvent.OnConflictsDetected, AddressOf OnConflictsDetected
    End Sub
    
    ''' <summary>
    ''' Pseudocode:
    ''' - Loop through all conflict classes after the reconcile.
    ''' - Loop through every UpdateUpdate conflict on the class.
    ''' - Determine if geometry is in conflict on the feature.
    ''' - If so, merge geometries together (handling errors) and store the feature.
    ''' </summary>

    Public Sub OnConflictsDetected(ByRef conflictsRemoved As Boolean, ByRef errorOccurred As Boolean, _
                                   ByRef errorString As String)
        Try
        Dim versionEdit4 As IVersionEdit4 = CType(featureWorkspace, IVersionEdit4)
        
        ' Get the various versions on which to output information.
        Dim commonAncestorFWorkspace As IFeatureWorkspace = CType(versionEdit4.CommonAncestorVersion, IFeatureWorkspace)
        Dim preReconcileFWorkspace As IFeatureWorkspace = CType(versionEdit4.PreReconcileVersion, IFeatureWorkspace)
        Dim reconcileFWorkspace As IFeatureWorkspace = CType(versionEdit4.ReconcileVersion, IFeatureWorkspace)
        
        Dim enumConflictClass As IEnumConflictClass = versionEdit4.ConflictClasses
        Dim conflictClass As IConflictClass = enumConflictClass.Next()
        Do While Not conflictClass Is Nothing
            Dim dataset As IDataset = CType(conflictClass, IDataset)
            
            ' Make sure the class is a feature class.
            If (dataset.Type = esriDatasetType.esriDTFeatureClass) Then
                Dim datasetName As String = dataset.Name
                Dim featureClass As IFeatureClass = featureWorkspace.OpenFeatureClass(datasetName)
                Console.WriteLine("Conflicts on feature class {0}", datasetName)
                
                ' Get all UpdateUpdate conflicts.
                Dim updateUpdates As ISelectionSet = conflictClass.UpdateUpdates
                If (updateUpdates.Count > 0) Then
                    ' Get conflict feature classes on the three reconcile versions.
                    Dim featureClassPreReconcile As IFeatureClass = preReconcileFWorkspace.OpenFeatureClass(datasetName)
                    Dim featureClassReconcile As IFeatureClass = reconcileFWorkspace.OpenFeatureClass(datasetName)
                    Dim featureClassCommonAncestor As IFeatureClass = commonAncestorFWorkspace.OpenFeatureClass(datasetName)
                    
                    ' Iterate through each OID, outputting information.
                    Dim enumIDs As IEnumIDs = updateUpdates.IDs
                    Dim oid As Integer = enumIDs.Next()
                    Do While Not oid = -1 ' Loop through all conflicting features.
                        Console.WriteLine("UpdateUpdate conflicts on feature {0}", oid)
                        
                        ' Get conflict feature on the three reconcile versions.
                        Dim featurePreReconcile As IFeature = featureClassPreReconcile.GetFeature(oid)
                        Dim featureReconcile As IFeature = featureClassReconcile.GetFeature(oid)
                        Dim featureCommonAncestor As IFeature = featureClassCommonAncestor.GetFeature(oid)
                        
                        ' Check to make sure each shape is different than the common ancestor (conflict is on shape field).
                        If IsShapeInConflict(featureCommonAncestor, featurePreReconcile, featureReconcile) Then
                            Console.WriteLine(" Shape attribute has changed on both versions...")
                            
                            ' Geometries are in conflict ... merge geometries.
                            Try
                            Dim constructMerge As IConstructMerge = New GeometryEnvironmentClass()
                            Dim newGeometry As IGeometry = constructMerge.MergeGeometries(featureCommonAncestor.ShapeCopy, featureReconcile.ShapeCopy, featurePreReconcile.ShapeCopy)
                            
                            ' Setting new geometry as a merge between the two versions.
                            Dim feature As IFeature = featureClass.GetFeature(oid)
                            feature.Shape = newGeometry
                            feature.Store()
                            updateUpdates.RemoveList(1, oid)
                            conflictsRemoved = True
                            Catch comExc As COMException
                            ' Check if the error is from overlapping edits.
                            If comExc.ErrorCode = fdoError.FDO_E_WORKSPACE_EXTENSION_DATASET_CREATE_FAILED Or _
                                                  comExc.ErrorCode = fdoError.FDO_E_WORKSPACE_EXTENSION_DATASET_DELETE_FAILED Then
                                ' Edited areas overlap.
                                Console.WriteLine("Error from overlapping edits on feature {0}", oid)
                                Console.WriteLine(" Error Message: {0}", comExc.Message)
                                Console.WriteLine(" Can't merge overlapping edits to same feature.")
                            Else
                                ' Unexpected COM exception, throw this to the exception handler.
                                Throw comExc
                            End If
                            End Try
                        Else
                            Console.WriteLine(" Shape field not in conflict: merge not necessary ... ")
                        End If
                        
                        ' Get ObjectID of the next conflicting feature.
                        oid = enumIDs.Next()
                    Loop
                End If
            End If
            
            ' Get the next conflict class.
            conflictClass = enumConflictClass.Next()
        Loop
        Catch comExc As COMException
        Console.WriteLine("Error Message: {0}, Error Code: {1}", comExc.Message, comExc.ErrorCode)
        Catch exc As Exception
        Console.WriteLine("Unhandled Exception: {0}", exc.Message)
        End Try
    End Sub
    
    ' Method to determine if the shape field is in conflict.

    Private Function IsShapeInConflict(ByVal commonAncestorFeature As IFeature, ByVal preReconcileFeature As IFeature, _
                                       ByVal reconcileFeature As IFeature) As Boolean
        ' 1st check: Common Ancestor with PreReconcile.
        ' 2nd check: Common Ancestor with Reconcile.
        ' 3rd check: Reconcile with PreReconcile (case of same change on both versions).
        If IsGeometryEqual(commonAncestorFeature.ShapeCopy, preReconcileFeature.ShapeCopy) Or _
                              IsGeometryEqual(commonAncestorFeature.ShapeCopy, reconcileFeature.ShapeCopy) Or _
                              IsGeometryEqual(reconcileFeature.ShapeCopy, preReconcileFeature.ShapeCopy) Then
            Return False
        Else
            Return True
        End If
    End Function
    
    ' Method returning if two shapes are equal to one another.

    Private Function IsGeometryEqual(ByVal shape1 As IGeometry, ByVal shape2 As IGeometry) As Boolean
        If shape1 Is Nothing And shape2 Is Nothing Then
            Return True
        ElseIf shape1 Is Nothing Xor shape2 Is Nothing Then
            Return False
        Else
            Dim clone1 As IClone = CType(shape1, IClone)
            Dim clone2 As IClone = CType(shape2, IClone)
            Return clone1.IsEqual(clone2)
        End If
    End Function
    
End Class


See Also:

How to wire ArcObjects .NET events
Listening to versioned events




To use the code in this topic, reference the following assemblies in your Visual Studio project. In the code files, you will need using (C#) or Imports (VB .NET) directives for the corresponding namespaces (given in parenthesis below if different from the assembly name):
Development licensing Deployment licensing
ArcView ArcView
ArcEditor ArcEditor
ArcInfo ArcInfo
Engine Developer Kit Engine Runtime