How to copy or load data and preserve GlobalID values


This document was published with and applies to ArcGIS 9.3.
A 10 version also exists.
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 as well as custom applications that distribute data are examples of when this is required. The article below describes a specific example in involving geodatabase replication:

http://support.esri.com/index.cfm?fa=knowledgebase.techarticles.articleShow&d=34200

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 wish 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 the 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 article describes techniques that can be used preserve GlobalIDs when loading as well as copying data.


Development licensing Deployment licensing
ArcEditor ArcEditor
ArcInfo ArcInfo
Engine Developer Kit Engine Runtime: Geodatabase Update

To use the code in this article, the following namespaces must be referenced via the using (C#) or Imports (VB.NET) statements. It is also necessary to add the corresponding references to the project in order to gain access to these APIs.

In this topic

Copying and loading data while preserving GlobalID values

When copying data in ArcGIS, copy and paste, data extraction and 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 it 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 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 article describes some approaches that use these methods.

Determine the best approach

This article describes 2 approaches for preserving GlobalID values. The following describes each approach:
 
Approach 1: Convert between GlobalID and GUID - Methods are provided that allow you to convert a GlobalID column to a GUID column and also to 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.
 
See the Convert between GlobalID and GUID section below for more details.
 
 
Approach 2: Import using the DataChangesImporter - 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 wish to load and then how to import the delta files. The following describes details to consider when choosing this approach:
 
- This is approach supports data loading but not data copying. 
- The target must be versioned ArcSDE data and can include simple or non-simple features (i.e. geometric networks)
- This approach account for cases where data with the same GlobalID is loaded more than once
- This approach requires field names must match when loading data.
 
See the Import using the DataChangesImporter section below for more details.
 

Approach 1: Convert between GlobalID and GUID columns

This approach involves converting GlobalID columns to GUID columns, copying or loading and 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, you will need to do this to each feature class or table that you wish to copy. If you are loading data, you need to perform the conversion to the datasets into which you want to load the data.
 
The following code converts a GlobalID column to a GUID column.  It takes the workspace and the 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 then the 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 which you converted in the previous step. They also support loading from any geodatabase source including file geodatabases and personal geodatabases.
 
With the data copied or loaded, you can then use the code below to convert the GUID columns back to GlobalID columns. This code takes the workspace, the name of the dataset and the name of the GUID column to convert. Like the code above, the workspace must be an ArcSDE workspace and the dataset must be unversioned and not involved in an existing replica. Note that if there are any duplicate values in the GUID column, the conversion will error. Here you will need to make changes such that the values are unique before attempting to converting 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

Apporach 2: Import using the DataChangesImporter

This approach involves using a delta file in order 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 code below 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.
Also note that the target dataset must be owned by the user referenced by the target workspace. The code generates an XML delta file which includes inserts derived from the rows in 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 of the code provided in this article:

[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