Copying or loading data while preserving GlobalID values (ArcObjects .NET 10.6 SDK)
ArcObjects Help for .NET developers > Developing with ArcGIS > Learning ArcObjects > Managing data > Working with feature data > Distributed geodatabases > Replication > Copying or loading data while preserving GlobalID values (ArcObjects .NET 10.6 SDK)

Copying or loading data while preserving GlobalID values


Summary
There are some cases where feature classes and tables that have a GlobalID column need to be copied and the GlobalID values preserved. Geodatabase replication and custom applications that distribute data are examples of when this is required.
In this example, one way replicas are created where edits from many source geodatabases are applied to a single target geodatabase. In this case, the data you want to replicate starts off existing only in the source geodatabases. The first step is to load all of the data from the source geodatabases into the target geodatabase. Once this is done, the data in the source and target geodatabases match and replicas can be created using the register only option. This option registers the replicas without copying data. From that point onward, the target geodatabase can be kept in sync with the source geodatabases using replica synchronization.
For this example, it is important to ensure that the GlobalID values in the source and target geodatabases match before creating the replicas. If the GlobalID values do not match, the synchronization process will not be able to find the rows in the target in which to apply changes. One way to achieve matching GlobalID values is to add GlobalID columns in all geodatabases before loading data into the target geodatabase, then when loading data into the target, you must use a technique that preserves the GlobalID values from the source geodatabases. This topic describes techniques that can be used to preserve GlobalIDs when loading or copying data.

In this topic


About copying or loading data while preserving GlobalID values

When copying data in ArcGIS, copy and paste, data extraction, and Extensible Markup Language (XML) workspace import preserves the GlobalID values. When using these methods, the GlobalID in the original matches the GlobalID in the copy. Other data copying methods, such as the export data command from the layer context menu in ArcMap or the copy features geoprocessing tool, do not preserve the GlobalID values. Here, a new GlobalID is assigned in the copy and therefore, does not match the value in the original.

Data loading operations involve appending rows into a feature class or table from other feature classes or tables. The simple data loader in ArcCatalog and the append geoprocessing tool are examples of ways this can be done in ArcGIS. These data loading tools assign new GlobalID values to the appended rows and therefore, do not preserve the original GlobalID values.

There are some ArcObjects methods, however, that can be used either on their own or in conjunction with commands in ArcGIS to preserve GlobalIDs while copying or loading data. This topic describes some approaches that use these methods.

Determine the best approach

This topic describes two approaches for preserving GlobalID values. The following describes each approach:
  • Convert between GlobalID and GUID (approach 1)—Methods are provided that allow you to convert a GlobalID column to a globally unique identifier (GUID) column and convert a GUID column to a GlobalID column. This can be used in conjunction with other tools in ArcGIS to load or copy data, and preserve GlobalID values. The following describes details to consider when choosing this approach:
    • For copying data, the source and target must be unversioned, simple ArcSDE data.
    • For data loading, the target must be unversioned, simple ArcSDE data.
    • This approach does not account for cases where data with the same GlobalID is loaded more than once.
    • The data loading tools referenced in this approach allow you to map fields with different names.
  • Import using the DataChangesImporter (approach 2)—The DataChangesImporter class allows you to import changes from a delta file where the GlobalIDs of the inserted rows are preserved. This approach describes how to create these delta files from the source data that you want to load, then how to import the delta files. The following describes details to consider when choosing this approach:
    • This approach supports data loading but not data copying. 
    • The target must be versioned ArcSDE data, and can include simple or non-simple features (that is, geometric networks).
    • This approach accounts for cases where data with the same GlobalID is loaded more than once.
    • This approach requires that field names must match when loading data.

Convert between GlobalID and GUID columns (approach 1)

This approach involves converting GlobalID columns to GUID columns, copying or loading, then converting back to GlobalID columns.
The first step in this approach is to convert GlobalID columns to GUID columns. Like a GlobalID column, a GUID column only accepts properly formatted GUID values. However, values in a GUID column can be edited while values in a GlobalID column are system controlled and read-only. Data copying and loading tools, which are described later in the approach, can therefore preserve the values in the GUID column.
If you are copying data, do this to each feature class or table that you want to copy. If you are loading data, perform the conversion to the datasets into which you want to load the data.
The following code example converts a GlobalID column to a GUID column. It takes the workspace and dataset name that contains the GlobalID column that you want to convert as parameters. The workspace must be an ArcSDE workspace and the dataset must be unversioned, and not involved in an existing replica. You can also provide a fully qualified datasetName if the connected workspace user does not own the data. 
[C#]
// Converts a GlobalID field to a GUID field.
public static void ConvertGlobalIdToGuid(IWorkspace workspace, String datasetName)
{
    // Open the table.
    IFeatureWorkspace featureWorkspace=(IFeatureWorkspace)workspace;
    ITable table=featureWorkspace.OpenTable(datasetName);

    // Get the GlobalID field.
    IClassEx classEx=(IClassEx)table;
    if (!classEx.HasGlobalID)
    {
        throw new Exception(String.Format("No GlobalID column in table: {0}.",
            datasetName));
    }
    String globalIDFieldName=classEx.GlobalIDFieldName;

    // Convert the GlobalID column to a GUID column.
    IClassSchemaEditEx classSchemaEditEx=(IClassSchemaEditEx)table;
    classSchemaEditEx.UnregisterGlobalIDColumn(globalIDFieldName);
}
[VB.NET]
' Converts a GlobalID field to a GUID field.
Public Shared Sub ConvertGlobalIdToGuid(ByVal workspace As IWorkspace, ByVal datasetName As String)
' Open the table.
Dim featureWorkspace As IFeatureWorkspace=CType(workspace, IFeatureWorkspace)
Dim table As ITable=featureWorkspace.OpenTable(datasetName)

' Get the GlobalID field.
Dim classEx As IClassEx=CType(table, IClassEx)
If Not classEx.HasGlobalID Then
    Throw New Exception(String.Format("No GlobalID column in table: {0}.", datasetName))
End If
Dim globalIDFieldName As String=classEx.GlobalIDFieldName

' Convert the GlobalID column to a GUID column.
Dim classSchemaEditEx As IClassSchemaEditEx=CType(table, IClassSchemaEditEx)
classSchemaEditEx.UnregisterGlobalIDColumn(globalIDFieldName)
End Sub
Once the columns are converted, you can copy the feature classes or tables, or start loading data.
If you are loading data, you can use data loading tools in ArcGIS, such as the append geoprocessing tool or the simple data loader in ArcCatalog. These tools can map source GUID and GlobalID columns to the target GUID column that you previously converted. They also support loading from any geodatabase source including file geodatabases and personal geodatabases.
With the data copied or loaded, you can use the following code example to convert the GUID columns back to GlobalID columns. This code takes the workspace, dataset name, and GUID column name to convert. Like the preceding code example, the workspace must be an ArcSDE workspace, and the dataset must be unversioned and not involved in an existing replica. If there are any duplicate values in the GUID column, the conversion fails. Here, make changes so the values are unique before attempting to convert the column again.
[C#]
// Converts a GUID field to a GlobalID field.
public static void ConvertGuidToGlobalId(IWorkspace workspace, String datasetName,
    String guidFieldName)
{
    // Open the table.
    IFeatureWorkspace featureWorkspace=(IFeatureWorkspace)workspace;
    ITable table=featureWorkspace.OpenTable(datasetName);

    // Get the GUID field to convert.
    IFields fields=table.Fields;
    int guidFieldIndex=fields.FindField(guidFieldName);
    IField guidField=fields.get_Field(guidFieldIndex);
    if (guidField.Type != esriFieldType.esriFieldTypeGUID)
    {
        throw new Exception(String.Format("Field {0} is not a GUID field.",
            guidFieldName));
    }

    // Convert the GUID column to a GlobalID column.
    IClassSchemaEditEx classSchemaEditEx=(IClassSchemaEditEx)table;
    classSchemaEditEx.RegisterGlobalIDColumn(guidField.Name);
}
[VB.NET]
' Converts a GUID field to a GlobalID field.
Public Shared Sub ConvertGuidToGlobalId(ByVal workspace As IWorkspace, ByVal datasetName As String, ByVal guidFieldName As String)
' Open the table.
Dim featureWorkspace As IFeatureWorkspace=CType(workspace, IFeatureWorkspace)
Dim table As ITable=featureWorkspace.OpenTable(datasetName)

' Get the GUID field to convert.
Dim fields As IFields=table.Fields
Dim guidFieldIndex As Integer=fields.FindField(guidFieldName)
Dim guidField As IField=fields.Field(guidFieldIndex)
If guidField.Type <> esriFieldType.esriFieldTypeGUID Then
    Throw New Exception(String.Format("Field {0} is not a GUID field.", guidFieldName))
End If

' Convert the GUID column to a GlobalID column.
Dim classSchemaEditEx As IClassSchemaEditEx=CType(table, IClassSchemaEditEx)
classSchemaEditEx.RegisterGlobalIDColumn(guidField.Name)
End Sub

Import using the DataChangesImporter (approach 2)

This approach involves using a delta file to preserve GlobalID values when loading data. Here, the rows in the source datasets are written to a delta file as inserts. The DataChangesImporter coclass is then used to load inserts into the target datasets. If an insert in the delta file has the same GlobalID value as a row in the target, it is applied as an update. Thus, cases where rows with the same GlobalID are loaded more than once are accounted for.
The approach does not convert the GlobalID column, and supports both simple and complex geodatabase feature types. It requires the target datasets to be versioned and does not give you the ability to match fields with different names. A field name in the delta file that does not match a field name in the target feature class or table is skipped.
The following code example uses this approach to load data. It takes a source and target workspace, and a source and target dataset as parameters. The source workspace can reference any geodatabase workspace including file geodatabase and personal geodatabases. The target workspace and dataset must reference a versioned ArcSDE feature class or table.
The target dataset must be owned by the user referenced by the target workspace. The code generates an XML delta file that includes inserts derived from the rows in the source dataset. The XML delta file is created in the directory specified by the TEMP environment variable. It is then imported into the target workspace where the inserts are applied to the target dataset.
[C#]
// Import data while preserving GlobalID values.
static void ImportWithGlobalIDs(IWorkspace sourceWorkspace, String sourceDatasetName,
    IWorkspace targetWorkspace, String targetDatasetName)
{
    // Open the source dataset.
    IFeatureWorkspace sourceFeatureWorkspace=(IFeatureWorkspace)sourceWorkspace;
    ITable sourceTable=sourceFeatureWorkspace.OpenTable(sourceDatasetName);

    // Create a name object for the target workspace.
    IDataset dataset=(IDataset)targetWorkspace;
    IWorkspaceName targetWorkspaceName=(IWorkspaceName)dataset.FullName;

    // Initialize a TableDataChanges object with inserts.
    ITableDataChangesInfo tableDataChangesInfo=new TableDataChangesInfoClass();
    tableDataChangesInfo.Init(targetDatasetName, sourceTable, null, null);
    ITablesDataChanges tablesDataChanges=new TablesDataChangesClass();
    tablesDataChanges.Init(esriReplicaModelType.esriModelTypeFullGeodatabase);
    tablesDataChanges.Add(tableDataChangesInfo);
    IDataChanges dataChanges=(IDataChanges)tablesDataChanges;

    // Use the TablesDataChanges to generate an updategram.
    String tempDirectory=Environment.GetEnvironmentVariable("TEMP");
    String deltaFile=Path.Combine(tempDirectory, "tmp_import_with_GlobalIDs.xml");
    IExportDataChanges dataChangesExporter=new DataChangesExporterClass();
    dataChangesExporter.ExportDataChanges(deltaFile,
        esriExportDataChangesOption.esriExportToXML, dataChanges, true);

    // Import the updategram into the target workspace.
    IDeltaDataChanges deltaDataChanges=new DeltaDataChangesClass();
    IDeltaDataChangesInit2 deltaDataChangesInit2=(IDeltaDataChangesInit2)
        deltaDataChanges;
    deltaDataChangesInit2.Init2(deltaFile,
        esriExportDataChangesOption.esriExportToXML, false);
    IImportDataChanges importDataChanges=new DataChangesImporterClass();
    importDataChanges.ImportDataChanges(targetWorkspaceName, deltaDataChanges, false,
        true);
}
[VB.NET]
' Import data while preserving GlobalID values.
Public Shared Sub ImportWithGlobalIDs(ByVal sourceWorkspace As IWorkspace, ByVal sourceDatasetName As String, _
                                      ByVal targetWorkspace As IWorkspace, ByVal targetDatasetName As String)

' Open the source dataset.
Dim sourceFeatureWorkspace As IFeatureWorkspace=CType(sourceWorkspace, IFeatureWorkspace)
Dim sourceTable As ITable=sourceFeatureWorkspace.OpenTable(sourceDatasetName)

' Create a name object for the target workspace.
Dim dataset As IDataset=CType(targetWorkspace, IDataset)
Dim targetWorkspaceName As IWorkspaceName=CType(dataset.FullName, IWorkspaceName)

' Initialize a TableDataChanges object with inserts.
Dim tableDataChangesInfo As ITableDataChangesInfo=New TableDataChangesInfoClass()
tableDataChangesInfo.Init(targetDatasetName, sourceTable, Nothing, Nothing)
Dim tablesDataChanges As ITablesDataChanges=New TablesDataChangesClass()
tablesDataChanges.Init(esriReplicaModelType.esriModelTypeFullGeodatabase)
tablesDataChanges.Add(tableDataChangesInfo)
Dim dataChanges As IDataChanges=CType(tablesDataChanges, IDataChanges)

' Use the TablesDataChanges to generate an updategram.
Dim tempDirectory As String=Environment.GetEnvironmentVariable("TEMP")
Dim deltaFile As String=Path.Combine(tempDirectory, "tmp_import_with_GlobalIDs.xml")
Dim dataChangesExporter As IExportDataChanges=New DataChangesExporterClass()
dataChangesExporter.ExportDataChanges(deltaFile, esriExportDataChangesOption.esriExportToXML, dataChanges, True)

' Import the updategram into the target workspace.
Dim deltaDataChanges As IDeltaDataChanges=New DeltaDataChangesClass()
Dim deltaDataChangesInit2 As IDeltaDataChangesInit2=CType(deltaDataChanges, IDeltaDataChangesInit2)
deltaDataChangesInit2.Init2(deltaFile, esriExportDataChangesOption.esriExportToXML, False)
Dim importDataChanges As IImportDataChanges=New DataChangesImporterClass()
importDataChanges.ImportDataChanges(targetWorkspaceName, deltaDataChanges, False, True)
End Sub

Complete code example

The following contains all code examples in this topic.
[C#]
// Converts a GlobalID field to a GUID field.
public static void ConvertGlobalIdToGuid(IWorkspace workspace, String datasetName)
{
    // Open the table.
    IFeatureWorkspace featureWorkspace=(IFeatureWorkspace)workspace;
    ITable table=featureWorkspace.OpenTable(datasetName);

    // Get the GlobalID field.
    IClassEx classEx=(IClassEx)table;
    if (!classEx.HasGlobalID)
    {
        throw new Exception(String.Format("No GlobalID column in table: {0}.",
            datasetName));
    }
    String globalIDFieldName=classEx.GlobalIDFieldName;

    // Convert the GlobalID column to a GUID column.
    IClassSchemaEditEx classSchemaEditEx=(IClassSchemaEditEx)table;
    classSchemaEditEx.UnregisterGlobalIDColumn(globalIDFieldName);
}

// Converts a GUID field to a GlobalID field.
public static void ConvertGuidToGlobalId(IWorkspace workspace, String datasetName,
    String guidFieldName)
{
    // Open the table.
    IFeatureWorkspace featureWorkspace=(IFeatureWorkspace)workspace;
    ITable table=featureWorkspace.OpenTable(datasetName);

    // Get the GUID field to convert.
    IFields fields=table.Fields;
    int guidFieldIndex=fields.FindField(guidFieldName);
    IField guidField=fields.get_Field(guidFieldIndex);
    if (guidField.Type != esriFieldType.esriFieldTypeGUID)
    {
        throw new Exception(String.Format("Field {0} is not a GUID field.",
            guidFieldName));
    }

    // Convert the GUID column to a GlobalID column.
    IClassSchemaEditEx classSchemaEditEx=(IClassSchemaEditEx)table;
    classSchemaEditEx.RegisterGlobalIDColumn(guidField.Name);
}

// Import data while preserving GlobalID values.
static void ImportWithGlobalIDs(IWorkspace sourceWorkspace, String sourceDatasetName,
    IWorkspace targetWorkspace, String targetDatasetName)
{
    // Open the source dataset.
    IFeatureWorkspace sourceFeatureWorkspace=(IFeatureWorkspace)sourceWorkspace;
    ITable sourceTable=sourceFeatureWorkspace.OpenTable(sourceDatasetName);

    // Create a name object for the target workspace.
    IDataset dataset=(IDataset)targetWorkspace;
    IWorkspaceName targetWorkspaceName=(IWorkspaceName)dataset.FullName;

    // Initialize a TableDataChanges object with inserts.
    ITableDataChangesInfo tableDataChangesInfo=new TableDataChangesInfoClass();
    tableDataChangesInfo.Init(targetDatasetName, sourceTable, null, null);
    ITablesDataChanges tablesDataChanges=new TablesDataChangesClass();
    tablesDataChanges.Init(esriReplicaModelType.esriModelTypeFullGeodatabase);
    tablesDataChanges.Add(tableDataChangesInfo);
    IDataChanges dataChanges=(IDataChanges)tablesDataChanges;

    // Use the TablesDataChanges to generate an updategram.
    String tempDirectory=Environment.GetEnvironmentVariable("TEMP");
    String deltaFile=Path.Combine(tempDirectory, "tmp_import_with_GlobalIDs.xml");
    IExportDataChanges dataChangesExporter=new DataChangesExporterClass();
    dataChangesExporter.ExportDataChanges(deltaFile,
        esriExportDataChangesOption.esriExportToXML, dataChanges, true);

    // Import the updategram into the target workspace.
    IDeltaDataChanges deltaDataChanges=new DeltaDataChangesClass();
    IDeltaDataChangesInit2 deltaDataChangesInit2=(IDeltaDataChangesInit2)
        deltaDataChanges;
    deltaDataChangesInit2.Init2(deltaFile,
        esriExportDataChangesOption.esriExportToXML, false);
    IImportDataChanges importDataChanges=new DataChangesImporterClass();
    importDataChanges.ImportDataChanges(targetWorkspaceName, deltaDataChanges, false,
        true);
}
[VB.NET]
' Converts a GlobalID field to a GUID field.
Public Shared Sub ConvertGlobalIdToGuid(ByVal workspace As IWorkspace, ByVal datasetName As String)
' Open the table.
Dim featureWorkspace As IFeatureWorkspace=CType(workspace, IFeatureWorkspace)
Dim table As ITable=featureWorkspace.OpenTable(datasetName)

' Get the GlobalID field.
Dim classEx As IClassEx=CType(table, IClassEx)
If Not classEx.HasGlobalID Then
    Throw New Exception(String.Format("No GlobalID column in table: {0}.", datasetName))
End If
Dim globalIDFieldName As String=classEx.GlobalIDFieldName

' Convert the GlobalID column to a GUID column.
Dim classSchemaEditEx As IClassSchemaEditEx=CType(table, IClassSchemaEditEx)
classSchemaEditEx.UnregisterGlobalIDColumn(globalIDFieldName)
End Sub

' Converts a GUID field to a GlobalID field.
Public Shared Sub ConvertGuidToGlobalId(ByVal workspace As IWorkspace, ByVal datasetName As String, ByVal guidFieldName As String)
' Open the table.
Dim featureWorkspace As IFeatureWorkspace=CType(workspace, IFeatureWorkspace)
Dim table As ITable=featureWorkspace.OpenTable(datasetName)

' Get the GUID field to convert.
Dim fields As IFields=table.Fields
Dim guidFieldIndex As Integer=fields.FindField(guidFieldName)
Dim guidField As IField=fields.Field(guidFieldIndex)
If guidField.Type <> esriFieldType.esriFieldTypeGUID Then
    Throw New Exception(String.Format("Field {0} is not a GUID field.", guidFieldName))
End If

' Convert the GUID column to a GlobalID column.
Dim classSchemaEditEx As IClassSchemaEditEx=CType(table, IClassSchemaEditEx)
classSchemaEditEx.RegisterGlobalIDColumn(guidField.Name)
End Sub

' Import data while preserving GlobalID values.
Public Shared Sub ImportWithGlobalIDs(ByVal sourceWorkspace As IWorkspace, ByVal sourceDatasetName As String, _
                                      ByVal targetWorkspace As IWorkspace, ByVal targetDatasetName As String)

' Open the source dataset.
Dim sourceFeatureWorkspace As IFeatureWorkspace=CType(sourceWorkspace, IFeatureWorkspace)
Dim sourceTable As ITable=sourceFeatureWorkspace.OpenTable(sourceDatasetName)

' Create a name object for the target workspace.
Dim dataset As IDataset=CType(targetWorkspace, IDataset)
Dim targetWorkspaceName As IWorkspaceName=CType(dataset.FullName, IWorkspaceName)

' Initialize a TableDataChanges object with inserts.
Dim tableDataChangesInfo As ITableDataChangesInfo=New TableDataChangesInfoClass()
tableDataChangesInfo.Init(targetDatasetName, sourceTable, Nothing, Nothing)
Dim tablesDataChanges As ITablesDataChanges=New TablesDataChangesClass()
tablesDataChanges.Init(esriReplicaModelType.esriModelTypeFullGeodatabase)
tablesDataChanges.Add(tableDataChangesInfo)
Dim dataChanges As IDataChanges=CType(tablesDataChanges, IDataChanges)

' Use the TablesDataChanges to generate an updategram.
Dim tempDirectory As String=Environment.GetEnvironmentVariable("TEMP")
Dim deltaFile As String=Path.Combine(tempDirectory, "tmp_import_with_GlobalIDs.xml")
Dim dataChangesExporter As IExportDataChanges=New DataChangesExporterClass()
dataChangesExporter.ExportDataChanges(deltaFile, esriExportDataChangesOption.esriExportToXML, dataChanges, True)

' Import the updategram into the target workspace.
Dim deltaDataChanges As IDeltaDataChanges=New DeltaDataChangesClass()
Dim deltaDataChangesInit2 As IDeltaDataChangesInit2=CType(deltaDataChanges, IDeltaDataChangesInit2)
deltaDataChangesInit2.Init2(deltaFile, esriExportDataChangesOption.esriExportToXML, False)
Dim importDataChanges As IImportDataChanges=New DataChangesImporterClass()
importDataChanges.ImportDataChanges(targetWorkspaceName, deltaDataChanges, False, True)
End Sub


See Also:

Set up one way replicas to send changes from many source geodatabases to a single target geodatabase




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
ArcGIS Desktop Standard ArcGIS Desktop Standard
ArcGIS Desktop Advanced ArcGIS Desktop Advanced
Engine Developer Kit Engine: Geodatabase Update