Overview
The release of ArcGIS Pro SDK 3.1 brings improvements to the Data Definition Language (DDL) API for geodatabases schema management. This API allows developers to create, modify, and delete geodatabases and their schema programmatically from scratch. Developers who rely on traditional geoprocessing tools for managing workspaces, datasets, fields, domains, subtypes, and relationship classes can use the DDL API to automate and extend the functionality of ArcGIS Pro.
The following functionalities are available on the specified geodatabase types:
Capability | File Geodatabase | Mobile Geodatabase | Enterprise Geodatabase | Memory Geodatabase |
Tables (create, modify, rename, delete) | ✔ | ✔ | ✔ | ✔ |
Feature classes (create, modify, rename, delete) | ✔ | ✔ | ✔ | ✔ |
Feature datasets (create, change contents, rename, delete) | ✔ | ✔ | ✔ | |
Domains (create, modify, rename, delete) | ✔ | ✔ | ✔ | ✔ |
Annotation feature classes (create, modify, rename, delete) | ✔ | ✔ | ✔ | |
Subtypes (create, modify, delete) | ✔ | ✔ | ✔ | ✔ |
Relationship classes (create, modify, rename, delete) | ✔ | ✔ | ✔ | |
Indexes (create, delete) | ✔ | ✔ | ✔ | |
Geodatabases (create, delete) | ✔ | ✔ | ✔ |
Usage pattern
The DDL routines follow a common pattern. First, a Description
class is used to specify the schema object to be created or deleted. The Description
objects are either created by assigning desired values to a set of properties and parameters, or using existing schema objects, including tokens or existing definitions.
In this example, you will create a description of a table with fields. A TableDescription
object describes a table. One property of a table description is a list of FieldDescription
objects to specify the fields.
// Create a FieldDescription for the InspectionDate field
FieldDescription inspectionDateFieldDescription = new FieldDescription("InspectionDate", FieldType.Date)
{
AliasName = "Inspection Date"
};
TableDescription inspectionTableDescription = new TableDescription(
"Inspections",
new List<FieldDescription> { inspectionDateFieldDescription }
);
To obtain the TableDescription
of a table that exists in the geodatabase, pass the Definition
object to the constructor.
TableDescription inspectionTableDescription = new TableDescription(inspectionTableDefinition);
The schema objects are created, modified, and deleted using a SchemaBuilder
object, which is constructed from the Geodatabase
object where the schema operations will take place.
// Create a SchemaBuilder Object
SchemaBuilder schemaBuilder = new SchemaBuilder(geodatabase);
DDL operations for creation are enqueued with the schema builder using the Create
method.
// Add the creation of the table to our list of DDL tasks
TableToken tableToken = schemaBuilder.Create(tableDescription);
The set of DDL operations is then run with a call to Build
. If the process fails, the ErrorMessages
property of the SchemaBuilder
object can be queried to find out what went wrong.
// Run the DDL
bool success = schemaBuilder.Build();
IReadOnlyList<string> errorMessages = schemaBuilder.ErrorMessages;
When using the Modify method to modify schema, there are two common patterns to obtain a modified description to pass to the method:
- Copy properties and attributes from the current description to a new description object, except for the properties that need to be modified. This is often done if the property to modify is read-only in the description class.
- Directly modify the current description object if the property is not read-only.
Tokens
Each call to the Create
method returns a Token
object. These tokens are used to specify the dependencies between DDL operations. For example, the Create(FeatureDatasetDescription)
routine returns a FeatureDatasetToken
. Developers can use this token to create feature classes inside a feature dataset.
schemaBuilder.Create(FeatureDatasetDescription(FeatureDatasetToken), FeatureClassDescription)
The FeatureDatasetToken
is used to create a FeatureDatasetDescription
.
Tokens can also be used to create a Description
representing the dataset from the enqueued operation:
TableDescription anotherTableDescription = new TableDescription(tableToken)
Next, let’s look at some of the key workflows using the DDL API. The following code snippets show various patterns for some common operations—other patterns are also possible.
Workspaces
The DDL API supports creation and deletion of the memory and local workspaces. You can create and delete memory, file, and mobile geodatabases using static methods from the SchemaBuilder
class using the CreateGeodatabase
and DeleteGeodatabase
methods.
// Creates a file geodatabase at specified path
SchemaBuilder.CreateGeodatabase(new FileGeodatabaseConnectionPath(pathUri));
// Creates a mobile geodatabase at specified path
SchemaBuilder.CreateGeodatabase(new MobileGeodatabaseConnectionPath(pathUri));
// Creates the default memory geodatabase
SchemaBuilder.CreateGeodatabase(new MemoryConnectionProperties());
// Deletes a file geodatabase at specified path
SchemaBuilder.DeleteGeodatabase(new FileGeodatabaseConnectionPath(pathUri));
// Deletes a mobile geodatabase at specified path
SchemaBuilder.DeleteGeodatabase(new MobileGeodatabaseConnectionPath(pathUri));
// Deletes the default memory geodatabase
SchemaBuilder.DeleteGeodatabase(new MemoryConnectionProperties());
Fields
The FieldDescription
class in the DDL API allows developer to specify the schema of attribute fields, with support for a range of field types, including ObjectID and GlobalID fields, as well as features including domains and default values.
// Creating a domain field named as 'PipeMaterials' for materials used in pipes
FieldDescription pipeMaterialDomainField = FieldDescription.CreateDomainField(
"PipeMaterials",
new CodedValueDomainDescription(
"MaterialType",
FieldType.String,
new SortedList<object, string>(){{"Cu","Copper"}, {"Fe","Iron"}}
)
);
// Creating a double field named as 'PipeLength' with default value of 1.5
FieldDescription pipeLength = new FieldDescription("PipeLength", FieldType.Double);
pipeLength.SetDefaultValue(1.5);
To create a domain using the DDL API, see the Domains section.
Table, feature class, and feature dataset
The TableDescription
, FeatureClassDescription
, and AnnotationFeatureClassDescription
classes are used to create a standalone table, feature class, or an annotation feature class respectively with the DDL API.
Certain parameters are required in each constructor when creating these descriptions without a prior schema object such as a Token
or Description
. The TableDescription
class requires the table’s name and a set of attribute fields to create a standalone table. Similarly, constructing the FeatureClassDescription
object requires the feature class’s name, a set of attribute fields, and the shape information of a feature class. In addition to the information required for a feature class, constructing the AnnotationFeatureClassDescription
object requires the labels and their placement information.
These datasets can also be created inside a feature dataset if feature dataset information is provided.
Create a table and feature class
In this example, you will create a feature class. First, create a ShapeDescription
object to specify the geometry type and spatial reference. A FieldDescription
object is specified in the constructor, contained in the pipeLength
variable. Other fields required for this feature class are generated by default when building the schema.
// Create feature class called Pipe
ShapeDescription shapeDescription = new ShapeDescription(GeometryType.Polyline, SpatialReferenceBuilder.CreateSpatialReference(4326));
FeatureClassDescription featureClassDescription = new FeatureClassDescription(
"Pipe",
new List<FieldDescription> { pipeLength },
shapeDescription
);
schemaBuilder.Create(featureClassDescription);
bool success = schemaBuilder.Build();
Modify the table and feature class
To obtain the schema of the existing feature class, first get a FeatureClassDefinition
object (called featureClassDefinition
) from the datastore. Then add a field to the Description
by first creating a new Description
, as the list of fields is a read-only property, and call Modify()
on the description.
// Use definition to make a description object, and a field to the original feature class
FieldDescription pipeDiameter = new FieldDescription("PipeDiameter", FieldType.Double);
FeatureClassDescription originalDescription = new FeatureClassDescription(featureClassDefinition);
FeatureClassDescription modifiedDescription = new FeatureClassDescription(
originalDescription.Name,
originalDescription.FieldDescriptions.Append(pipeDiameter),
originalDescription.ShapeDescription
);
schemaBuilder.Modify(modifiedDescription);
bool success = schemaBuilder.Build();
Delete a table and feature class
Similar to the pattern for modifying feature classes, obtain Definitions
for the feature class using the datastore, and then create a description. Then call Delete()
on the descriptions.
// Use definition to make description object and delete it
FeatureClassDescription featureClassDescription = new FeatureClassDescription(featureClassDefinition);
schemaBuilder.Delete(featureClassDescription);
bool success = schemaBuilder.Build();
Domains
Attribute domains are rules that describes the acceptable values of a field for a given field type. They ensure data integrity by limiting the choice of values for a particular field.
The DDL API supports creation of both types of domains–Range and Coded Value. Range Domains specify a valid range of values for numeric and date attribute fields, and Coded Value Domain specify a valid set of values for any type of attribute field. Domains can also be modified or deleted using the DDL API. After creating a domain, they can be associated with fields via their FieldDescription
object.
Create domains
To create domains, first create a Range Domain description as well as a Coded Value Domain description using their respective constructors. The range domain will have a minimum value of 1.1 and a maximum value of 9.5. The coded value domain will have 2 coded values representing pipe materials.
// Create a range domain description
RangeDomainDescription rangeDomainDescription = new RangeDomainDescription("PipeLengthRangeDomain", FieldType.Double, 1.1, 9.5)
{
Description = "A new range domain",
};
// Create a coded value domain description
CodedValueDomainDescription codedValueDomainDescription = new CodedValueDomainDescription(
"MaterialType",
FieldType.String,
new SortedList<object, string>() { { "Cu", "Copper" }, { "Fe", "Iron" } }
);
schemaBuilder.Create(rangeDomainDescription);
schemaBuilder.Create(codedValueDomainDescription);
bool success = schemaBuilder.Build();
Modify domains
Because you are modifying properties of the domains that cannot be set, you will create a modified domain description and copy some properties over from the original. The original domain was created using a constructor, which takes the Domain
object itself as a parameter. The Domain
object can be obtained from the geodatabase. Note that the description of the Range Domain is not copied, and thus does not appear in the modified domain.
// Modify range of the range domain
RangeDomainDescription originalRangeDomainDescription = new RangeDomainDescription(rangeDomain);
RangeDomainDescription modifiedRangeDomainDescription = new RangeDomainDescription(
originalRangeDomainDescription.Name,
originalRangeDomainDescription.FieldType,
0.0, 10.0
);
// Modify coded values of the coded value domain
CodedValueDomainDescription originalCodedValueDomainDescription = new CodedValueDomainDescription(codedValueDomain);
SortedList<object, string> newCodedValuePairs = new SortedList<object, string>()
{
{ "Cu", "Copper" }, { "Fe", "Iron" }, { "Pvc", "Polyvinyl Chloride" }
};
CodedValueDomainDescription modifiedCodedValueDomainDescription = new CodedValueDomainDescription(
originalCodedValueDomainDescription.Name,
originalCodedValueDomainDescription.FieldType,
newCodedValuePairs
);
schemaBuilder.Modify(modifiedRangeDomainDescription);
schemaBuilder.Modify(modifiedCodedValueDomainDescription);
bool success = schemaBuilder.Build();
Delete Domains
Similar to the pattern for modifying domains, obtain Domain
objects for both the Range and Coded Value domain using the datastore and then create descriptions using them. Then call Delete()
on the descriptions.
RangeDomainDescription rangeDomainDescription = new RangeDomainDescription(rangeDomain);
CodedValueDomainDescription codedValueDomainDescription = new CodedValueDomainDescription(codedValueDomain);
schemaBuilder.Delete(rangeDomainDescription);
schemaBuilder.Delete(codedValueDomainDescription);
bool success = schemaBuilder.Build();
Subtypes
Subtypes are used to categorize objects in a single dataset that shares the same attributes. To add subtypes, you must have a subtype field in the dataset. The subtype field must be of long or short integer data type.
The DDL API provides patterns to assign an existing field as the subtype field and add subtype values in the dataset using the SubtypeFieldDescription
class. The API also allows for modifications of the subtype list and unassigning a field as the subtype field. The class constructor takes a field name and code-name (key-value) pair of subtypes as parameters.
Designate a field as the subtype field
Suppose a field called PipeType is added to the previously created Pipe feature class. Obtain the FeatureClassDefinition
object representing the table in order to modify it, and pass it into the FeatureClassDescription
constructor. To set PipeType as the subtype field, create a SubtypeFieldDescription
object with the field name as the first parameter of the constructor, and then call Modify()
.
// Assign the PipeType field as the subtype field
FeatureClassDescription featureClassDescription = new FeatureClassDescription(featureClassDefinition);
featureClassDescription.SubtypeFieldDescription = new SubtypeFieldDescription(
"PipeType",
new Dictionary<int, string>
{
{ 0, "water" },
{ 1, "sewage" }
}
);
schemaBuilder.Modify(featureClassDescription);
bool success = schemaBuilder.Build();
Modify subtypes
To modify the subtype, you will rely on the pattern of accessing the SubtypeFieldDescription
from the properties of the feature class’ FeatureClassDescription
. Then call Modify()
.
// Remove a subtype from the PipeType subtype field
FeatureClassDescription featureClassDescription = new FeatureClassDescription(featureClassDefinition);
featureClassDescription.SubtypeFieldDescription.Subtypes.Remove(1);
schemaBuilder.Modify(featureClassDescription);
bool success = schemaBuilder.Build();
Remove subtype designation
To modify the subtype, you will rely on the pattern of accessing the SubtypeFieldDescription
from the properties of the feature class’ FeatureClassDescription
. To remove the subtype, set the property to null
, and then call Modify()
.
// Unassign the PipeType field as the subtype field
FeatureClassDescription featureClassDescription = new FeatureClassDescription(featureClassDefinition);
featureClassDescription.SubtypeFieldDescription = null;
schemaBuilder.Modify(featureClassDescription);
bool success = schemaBuilder.Build();
Relationship Class
Relationship classes define the associations between objects in one feature class or table to objects in another feature class or table. ArcGIS Pro supports one-to-one, one-to-many, and many-to-many and may have attributes about the relationship itself.
For the DDL API, the RelationshipClassDescription
class describes the relationship class between two classes as well as the origin and foreign key fields participating in the relationship.
Attributed relationship classes can contain their own attributes for each relationship between objects in the origin and destination class and are described by the AttributedRelationshipDescription
class.
Create a relationship class
To create a relationship class from scratch without prior schema, description objects for both the origin table (pipeFeatureClassDescription
) and destination table (inspectionDateTableDescription
) are required for the constructor. You will also pass the name of the origin primary key field and origin foreign key field, which have been already created.
// Create relationship from pipes to inspection dates
FeatureClassDescription pipeFeatureClassDescription = new FeatureClassDescription(pipeFeatureClassDefinition);
TableDescription inspectionDateTableDescription = new TableDescription(inspectionDateTableDefinition);
RelationshipClassDescription relationshipClassDescription = new RelationshipClassDescription(
"PipeToInspectionDate",
pipeFeatureClassDescription, inspectionDateTableDescription,
RelationshipCardinality.OneToMany,
"PipeID", "InspectionID"
);
schemaBuilder.Create(relationshipClassDescription);
bool success = schemaBuilder.Build();
Modify relationship class
To obtain the schema of the existing relationship class, you will first get a RelationshipClassDefinition
object from the datastore. Then change the split policy property on a created RelationshipClassDescription
object and call Modify()
on the description.
// Modify relationship split policy
RelationshipClassDescription relationshipClassDescription = new RelationshipClassDescription(relationshipClassDefinition);
relationshipClassDescription.RelationshipSplitPolicy = RelationshipSplitPolicy.UseDefault;
schemaBuilder.Modify(relationshipClassDescription);
bool success = schemaBuilder.Build();
Delete a relationship class
Similar to the pattern for modifying relationship classes, obtain the RelationshipClassDefinition
object using the datastore, and then create a Description
object using it. Then call Delete()
on the description.
// Delete the relationship class
RelationshipClassDescription relationshipClassDescription = new RelationshipClassDescription(relationshipClassDefinition);
schemaBuilder.Delete(relationshipClassDescription);
bool success = schemaBuilder.Build();
Takeaway
The DDL API supports developers, users, and business partners who want to create and manipulate their datasets using the ArcGIS Pro SDK. Examples of workflows include storing results of operations in new datasets, creating temporary memory datastores for nonpersistent data, or programmatically modifying geodatabase schema based on user needs.
While geoprocessing tools and Python remain the preferred way to perform and automate large-scale schema operations, support for manipulating geodatabase schema directly in the ArcGIS Pro SDK provides you with another powerful way to extend the functionality of ArcGIS Pro. Please refer to the DDL Conceptual Document and the geodatabase Object Model Diagram for additional details.
Article Discussion: