ProConcepts DDL - Esri/arcgis-pro-sdk GitHub Wiki
The DDL API is used to create and delete geodatabases and geodatabase schema objects.
Language: C#
Subject: Geodatabase DDL
Contributor: ArcGIS Pro SDK Team <[email protected]>
Organization: Esri, http://www.esri.com
Date: 10/06/2024
ArcGIS Pro: 3.4
Visual Studio: 2022
- Introduction
- General Usage
- Tables and Feature Classes
- Domains
- Feature Datasets
- Annotation
- Subtypes
- Relationships
- Indexes
- Geodatabases
The ArcGIS Pro SDK provides a limited amount of geodatabase DDL (data definition language) functionality.
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, changing 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) | β | β | β |
No DDL operations are supported on other geodatabase or datastore types.
This API supports the most-commonly used DDL operations. Additional DDL operations must be performed using the geoprocessing API Geoprocessing API.
All of the classes described here can be found in the ArcGIS.Core.Data.DDL
namespace.
As with most of the geodatabase API, the classes and methods detailed here are intended to be called on the Main CIM Thread (MCT) as described in ProConcepts-Geodatabase.
The DDL routines follow a common pattern. A Description
class is used to specify the schema object to be created, modified, or deleted. For example, a TableDescription
describes a table. One of the properties 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"
};
Generally, Description objects are either hand-created using a set of properties, or are created using an existing schema object.
Schema modifications are made using a SchemaBuilder
object, which is constructed with a Geodatabase
. File, mobile, enterprise, and memory geodatabases support schema operations.
// Create a SchemaBuilder object
SchemaBuilder schemaBuilder = new SchemaBuilder(geodatabase);
DDL operations for creation are enqueued with the schema builder using a set of calls to Create
methods.
// Add the creation of the table to our list of DDL tasks
schemaBuilder.Create(tableDescription);
The set of DDL operations is then executed with a call to Build
. If the process fails, the ErrorMessages
property can be queried to find out what went wrong.
// Execute the DDL
bool success = schemaBuilder.Build();
DDL routines can fail for the same reason that the equivalent geoprocessing tool can do so. For example, if a table has outstanding edits, you cannot delete it.
In terms of failures, the Build
operation is not guaranteed to be atomic. For example, consider a build operation that creates two tables. If the second table creation fails, the first table would still be created. It is the responsibility of the caller to check the ErrorMessages
property and do any required clean-up in the case of failure. In the example above, if the second table creation fails, the caller may wish to delete the first table in subsequent DDL operations.
The same SchemaBuilder
object can be used for multiple sets of DDL operations, each executed by a call to Build
. If Build
is called multiple times, only messages from the most recent call can be retrieved with the ErrorMessages
property.
Each Create
method returns a Token
object. These tokens are used when you need to specify dependencies between DDL operations. For example, the Create(FeatureDatasetDescription)
routine returns a FeatureDatasetToken
. A developer could then create one or more feature classes, using Create(FeatureDatasetDescription, FeatureClassDescription)
. The FeatureDatasetDescription
would be created from the FeatureDatasetToken
. In this way, a single call to SchemaBuilder.Build
could create two tables and a relationship class between them. See the Feature Datasets section below for a code example of this scenario.
The Pro SDK can be used to create tables and feature classes. To begin, create a series of FieldDescription
objects.
Each of these represents the definition of a field to be created on the new table. You can create a FieldDescription
from an existing Field
or by individually setting properties. Static helper routines are provided for creating common types of fields. Some examples of creating fields are shown below:
// Create a FieldDescription for the InspectionDate field
FieldDescription inspectionDateFieldDescription = new FieldDescription("InspectionDate", FieldType.Date)
{
AliasName = "Inspection Date"
};
// This static helper routine creates a FieldDescription for a Domain field (from a pre-existing domain)
FieldDescription inspectionResultsFieldDescription = FieldDescription.CreateDomainField("InspectionResults", new CodedValueDomainDescription(inspectionResultsDomain));
inspectionResultsFieldDescription.AliasName = "Inspection Results";
After creating the desired set of FieldDescription
objects, the next step is to create a TableDescription
object representing the definition of the table itself.
The TableDescription
is passed to SchemaBuilder.Create
and then SchemaBuilder.Build
is used to actually create the table.
// Assemble a list of all of our field descriptions
List<FieldDescription> fieldDescriptions = new List<FieldDescription>()
{ globalIDFieldDescription, objectIDFieldDescription, inspectionDateFieldDescription, inspectionResultsFieldDescription, inspectionNotesFieldDescription };
// Create a TableDescription object to describe the table to create
TableDescription tableDescription = new TableDescription("PoleInspection", fieldDescriptions);
// Create a SchemaBuilder object
SchemaBuilder schemaBuilder = new SchemaBuilder(geodatabase);
// Add the creation of PoleInspection to our list of DDL tasks
schemaBuilder.Create(tableDescription);
// Execute the DDL
bool success = schemaBuilder.Build();
Although shown above, an ObjectID field is automatically added if one is not passed in.
Creating a feature class follows much of the same principles as creating a table. One addition is that you must create a ShapeDescription
object to represent the defintion of the shape field.
ShapeDescription
objects can be created from a set of properties or by using the FeatureClassDefinition
of an existing feature class.
// Create a ShapeDescription object
ShapeDescription shapeDescription = new ShapeDescription(GeometryType.Point, spatialReference);
// Alternatively, ShapeDescriptions can be created from another feature class. In this case, the new feature class will inherit the same shape properties of the existing class
ShapeDescription alternativeShapeDescription = new ShapeDescription(existingFeatureClass.GetDefinition());
The final step is to create a FeatureClassDescription
and build the feature class using the same pattern described above.
// Create a FeatureClassDescription object to describe the feature class to create
FeatureClassDescription featureClassDescription = new FeatureClassDescription("Cities", fieldDescriptions, shapeDescription);
// Create a SchemaBuilder object
SchemaBuilder schemaBuilder = new SchemaBuilder(geodatabase);
// Add the creation of the Cities feature class to our list of DDL tasks
schemaBuilder.Create(featureClassDescription);
// Execute the DDL
bool success = schemaBuilder.Build();
The DDL APIs can be used to modify an existing table or feature class.
First, create a new Description
object that matches the new definition of the table. The Name
property should match the name of an existing table. Then call the Modify
method on the SchemaBuilder
class. The example below shows how to add two fields to an existing feature class.
// Adding following fields to the 'Parcels' FeatureClass
// Tax_Code
// Parcel_Address
// The FeatureClass to modify
string featureClassName = "Parcels";
FeatureClassDefinition originalFeatureClassDefinition = geodatabase.GetDefinition<FeatureClassDefinition>(featureClassName);
FeatureClassDescription originalFeatureClassDescription = new FeatureClassDescription(originalFeatureClassDefinition);
// The two new fields to add on the 'Parcels' FeatureClass
FieldDescription taxCodeDescription = FieldDescription.CreateIntegerField("Tax_Code");
FieldDescription addressDescription = FieldDescription.CreateStringField("Parcel_Address", 150);
// Add new fields on the new FieldDescription list
List<FieldDescription> modifiedFieldDescriptions = new List<FieldDescription>(originalFeatureClassDescription.FieldDescriptions);
modifiedFieldDescriptions.Add(taxCodeDescription);
modifiedFieldDescriptions.Add(addressDescription);
// Create a new FeatureClassDescription with additional fields
FeatureClassDescription modifiedFeatureClassDescription = new FeatureClassDescription(originalFeatureClassDescription.Name, modifiedFieldDescriptions, originalFeatureClassDescription.ShapeDescription);
SchemaBuilder schemaBuilder = new SchemaBuilder(geodatabase);
// Update the 'Parcels' FeatureClass with newly added fields
schemaBuilder.Modify(modifiedFeatureClassDescription);
bool modifyStatus = schemaBuilder.Build();
if (!modifyStatus)
{
IReadOnlyList<string> errors = schemaBuilder.ErrorMessages;
}
To rename a table or feature class, create a Description
object that matches the table you wish to rename. Then call the Rename
method on the SchemaBuilder
class, as shown below:
// Renaming a table from 'Original_Table' to 'Renamed_Table'
string tableToBeRenamed = "Original_Table";
string tableRenameAs = "Renamed_Table";
TableDefinition tableDefinition = geodatabase.GetDefinition<TableDefinition>(tableToBeRenamed);
SchemaBuilder schemaBuilder = new SchemaBuilder(geodatabase);
// Table rename
schemaBuilder.Rename(new TableDescription(tableDefinition), tableRenameAs);
schemaBuilder.Build();
The SchemaBuilder.Modify
method is also used to add or remove fields into the dataset. The following example shows how to add additional fields to an existing table.
// Get table description from original table definition
TableDescription tableDescription = new TableDescription(parcelTableDefinition);
// Fields to be exist in the modified table
List<FieldDescription> fieldDescriptions = new List<FieldDescription> (tableDescription.FieldDescriptions);
// Field to be added - 'FloodRiskScale'
fieldDescriptions.Add(FieldDescription.CreateIntegerField("FloodRiskScale"));
// Modified table description
TableDescription modifiedTableDescription = new TableDescription(tableDescription.Name, fieldDescriptions);
// Update table with newly added fields
schemaBuilder.Modify(modifiedTableDescription);
// Execute DDL
schemaBuilder.Build();
The example belows shows to remove fields from a table.
// Get table description from original table definition
TableDescription tableDescription = new TableDescription(parcelTableDefinition);
// Fields to be retained in the modified table
FieldDescription taxFieldToBeRetained = new FieldDescription(parcelTableDefinition.GetFields().First(f => f.Name.Equals("TaxCode")));
List<FieldDescription> fieldsToBeRetained = new List<FieldDescription> { taxFieldToBeRetained };
// Modified table description
TableDescription modifiedTableDescription = new TableDescription(tableDescription.Name, fieldsToBeRetained);
// Remove all fields except the fields in retained in the list
schemaBuilder.Modify(modifiedTableDescription);
// Execute DDL
schemaBuilder.Build();
Field properties such as name, alias name, length, and type of some existing fields in tables or feature classes can be modified using SchemaBuilder.Modify(TableDescription, String, FieldDescription)
method. The following table summarizes allowed property changes based on geodatabase type.
Properties | File Geodatabase | Mobile Geodatabase | Enterprise Geodatabase | Memory Geodatabase |
---|---|---|---|---|
Field Name | β | β | β | |
Field Type (Table must be empty) | β | β | ||
Field Length (Must be String data type) | β | β | β | |
IsNullable (Table must be empty) | β | β | ||
Precision or Scale |
The following fields don't allow name, data type, and length alteration:
- Object ID and Global ID
- Shape, Shape area, and Shape length
- Subtype field
- Relationship class primary and foreign key fields
- Editor tracking fields
- Fields used in attribute rules
- Non-editable fields
- Other required fields
The maximum alias size is 255 characters long, and memory geodatabases don't allow empty strings as aliases.
The following example shows modifying the 'Parcel_Address' field in the Parcels feature class.
Field parcelAddress = featureClassDefinition.GetFields().FirstOrDefault(f => f.Name.Contains("Parcel_Address"));
// Update field's alias name and length
FieldDescription newParcelFieldDescription = new FieldDescription(parcelAddress)
{
AliasName = "Physical Property Address",
Length = 250
};
// Update the default value
newParcelFieldDescription.SetDefaultValue("123 Main St");
schemaBuilder.Modify(new TableDescription(featureClassDefinition), parcelAddress.Name, newParcelFieldDescription);
schemaBuilder.Build();
Tables and feature classes can be deleted using the DDL APIs. To do this, create a Description
and pass it to the SchemaBuilder.Delete
method. TableDescription
and FeatureClassDescription
objects can be easily constructed by passing in the definition of an existing table or feature class.
// Create a TableDescription object
TableDescription tableDescription = new TableDescription(table.GetDefinition());
// Create a SchemaBuilder object
SchemaBuilder schemaBuilder = new SchemaBuilder(geodatabase);
// Add the deletion of the table to our list of DDL tasks
schemaBuilder.Delete(tableDescription);
// Execute the DDL
bool success = schemaBuilder.Build();
Both coded value and range domains can be created with the DDL APIs in the Pro SDK.
The same pattern for other DDL operations applies hereβ create a Description
object and pass it to SchemaBuilder.Create
method. This example shows how to create a coded value domain for water pipe types.
CodedValueDomainDescription codedValueDomainDescription = new CodedValueDomainDescription("WaterPipeTypes", FieldType.String,
new SortedList<object, string> { { "Copper", "C_1" }, { "Steel", "S_2" } })
{
SplitPolicy = SplitPolicy.Duplicate,
MergePolicy = MergePolicy.DefaultValue
};
SchemaBuilder schemaBuilder = new SchemaBuilder(geodatabase);
// Create a coded value domain
CodedValueDomainToken codedValueDomainToken = schemaBuilder.Create(codedValueDomainDescription);
schemaBuilder.Build();
The SchemaBuilder
class can alter both coded value and range domains. First, create a modified description object and then pass it to the SchemaBuilder.Modify
method to update domain descriptions and add or remove domain values as described below.
// Updating description and value of a coded value domain
CodedValueDomain codedValueDomain = geodatabase.GetDomains().First(f => f.GetName().Equals("WaterPipeTypes")) as CodedValueDomain;
CodedValueDomainDescription codedValueDomainDescription = new CodedValueDomainDescription(codedValueDomain);
// Update the domain description
codedValueDomainDescription.Description = "Water Pipe Distribution Types";
// Add a new code/value pair
codedValueDomainDescription.CodedValuePairs.Add("A", "Aluminum");
schemaBuilder.Modify(codedValueDomainDescription);
schemaBuilder.Build();
To rename domains use SchemaBuilder.Rename
:
// Renaming range domain from 'WaterMainPipe' to 'WaterDistributionPipe'
RangeDomain rangeDomain = geodatabase.GetDomains().First(f => f.GetName().Equals("WaterMainPipe")) as RangeDomain;
// Assign the new name
schemaBuilder.Rename(new RangeDomainDescription(rangeDomain), "WaterDistributionPipe");
schemaBuilder.Build();
Likewise, domains can be deleted using SchemaBuilder.Delete
, passing in a CodedValueDomainDescription
or RangeDomainDescription
. Domains to be deleted cannot be in use by any fields in the geodatabase.
// Deleting 'WaterPipeTypes' domain
CodedValueDomain codedValueDomain = geodatabase.GetDomains().First(f => f.GetName().Equals("WaterPipeTypes")) as CodedValueDomain;
CodedValueDomainDescription codedValueDomainDescription = new CodedValueDomainDescription(codedValueDomain);
schemaBuilder.Delete(codedValueDomainDescription);
schemaBuilder.Build();
Feature datasets can be created and deleted using the same pattern as other DDL operations.
// Creating a FeatureDataset named as 'Parcel_Information'
SchemaBuilder schemaBuilder = new SchemaBuilder(geodatabase);
// Create a FeatureDataset named as 'Parcel Information'
FeatureDatasetDescription featureDatasetDescription = new FeatureDatasetDescription("Parcel_Information", SpatialReferences.WGS84);
schemaBuilder.Create(featureDatasetDescription);
// Build status
bool buildStatus = schemaBuilder.Build();
// Build errors
if (!buildStatus)
{
IReadOnlyList<string> errors = schemaBuilder.ErrorMessages;
}
You can also create a feature class directly inside a feature dataset. This example shows creating a feature dataset, and then creating a feature class within that feature dataset (by using a Token
as described above).
// Creating a FeatureDataset named as 'Parcel_Information' and a FeatureClass with name 'Parcels' in one operation
string featureDatasetName = "Parcel_Information";
string featureClassName = "Parcels";
SchemaBuilder schemaBuilder = new SchemaBuilder(geodatabase);
// Create a FeatureDataset token
FeatureDatasetDescription featureDatasetDescription = new FeatureDatasetDescription(featureDatasetName, SpatialReferences.WGS84);
FeatureDatasetToken featureDatasetToken = schemaBuilder.Create(featureDatasetDescription);
// Create a FeatureClass description
FeatureClassDescription featureClassDescription = new FeatureClassDescription(featureClassName,
new List<FieldDescription>()
{
new FieldDescription("Id", FieldType.Integer),
new FieldDescription("Address", FieldType.String)
},
new ShapeDescription(GeometryType.Point, SpatialReferences.WGS84));
// Create a FeatureClass inside a FeatureDataset
FeatureClassToken featureClassToken = schemaBuilder.Create(new FeatureDatasetDescription(featureDatasetToken), featureClassDescription);
// Build status
bool buildStatus = schemaBuilder.Build();
// Build errors
if (!buildStatus)
{
IReadOnlyList<string> errors = schemaBuilder.ErrorMessages;
}
Existing feature classes can be added and removed from a feature dataset using AddFeatureClass
and RemoveFeatureClass
respectively.
Finally, feature datasets can be renamed using SchemaBuilder.Rename
.
The DDL API also supports the creation of annotation feature classes. Similar to creating tables and feature classes, creation of the annotation feature class begins with a list of FieldDescription
objects and a ShapeDescription
object.
// Create fields
FieldDescription pipeGlobalID = FieldDescription.CreateGlobalIDField();
FieldDescription name = FieldDescription.CreateStringField("PipeName", 255);
// Create a list of all field descriptions
List<FieldDescription> fieldDescriptions = new List<FieldDescription> { pipeGlobalID, name };
// Create a ShapeDescription object
ShapeDescription shapeDescription = new ShapeDescription(GeometryType.Polygon, spatialReference);
After that, create a general placement properties object for the appropriate label engine.
This example shows how a CIMMaplexGeneralPlacementProperties
object can be used:
CIMMaplexGeneralPlacementProperties maplexGeneralPlacementProperties = new CIMMaplexGeneralPlacementProperties
{
AllowBorderOverlap = true,
PlacementQuality = MaplexQualityType.High,
DrawUnplacedLabels = true,
InvertedLabelTolerance = 1.0,
RotateLabelWithDisplay = true,
UnplacedLabelColor = new CIMRGBColor { R = 0, G = 255, B = 0, Alpha = 0.5f }
};
This example shows how a CIMStandardGeneralPlacementProperties
object can be used:
CIMStandardGeneralPlacementProperties standardGeneralPlacementProperties = new CIMStandardGeneralPlacementProperties
{
DrawUnplacedLabels = true,
InvertedLabelTolerance = 3.0,
RotateLabelWithDisplay = true,
UnplacedLabelColor = new CIMRGBColor { R = 255, G = 0, B = 0, Alpha = 0.5f }
};
Next, create a set of annotation label classes that describe how text labels should be created. This example shows creating a label object that uses green Tahoma text:
CIMLabelClass greenLabelClass = new CIMLabelClass
{
Name = "Green",
ExpressionTitle = "Expression-Green",
ExpressionEngine = LabelExpressionEngine.Arcade,
Expression = "$feature.OBJECTID",
ID = 1,
Priority = 0,
Visibility = true,
TextSymbol = new CIMSymbolReference
{
Symbol = new CIMTextSymbol()
{
Angle = 45,
FontType = FontType.Type1,
FontFamilyName = "Tahoma",
FontEffects = FontEffects.Normal,
HaloSize = 2.0,
Symbol = new CIMPolygonSymbol
{
SymbolLayers = new CIMSymbolLayer[]
{
new CIMSolidFill
{
Color = CIMColor.CreateRGBColor(0, 255, 0)
}
},
UseRealWorldSymbolSizes = true
}
},
MaxScale = 0,
MinScale = 0,
SymbolName = "TextSymbol-Green"
},
StandardLabelPlacementProperties = new CIMStandardLabelPlacementProperties
{
AllowOverlappingLabels = true,
LineOffset = 1.0
},
MaplexLabelPlacementProperties = new CIMMaplexLabelPlacementProperties
{
AlignLabelToLineDirection = true,
AvoidPolygonHoles = true
}
};
Create additional CIMLabelClass
objects as needed, and add them to a list (This snippet shows a longer example).
// Create a list of CIM label classes
List<CIMLabelClass> labelClasses = new List<CIMLabelClass> { greenLabelClass, blueLabelClass };
Finally, create an AnotationFeatureClassDescription
and build the annotation feature class using the same DDL create operation pattern described above:
// Create an annotation feature class description object to describe the annotation feature class to create
AnnotationFeatureClassDescription annotationFeatureClassDescription = new AnnotationFeatureClassDescription("WaterPipeAnnotation", fieldDescriptions, shapeDescription, maplexGeneralPlacementProperties, labelClasses)
{
IsAutoCreate = true,
IsSymbolIDRequired = false,
IsUpdatedOnShapeChange = true
};
// Create a SchemaBuilder object
SchemaBuilder schemaBuilder = new SchemaBuilder(geodatabase);
// Add the creation of the Cities annotation feature class to the list of DDL tasks
schemaBuilder.Create(annotationFeatureClassDescription);
// Execute the DDL
schemaBuilder.Build();
To create a feature-linked annotation feature class, the FeatureClassDescription
of the linked feature class should be passed during AnnotationFeatureClassDescription
creation.
// Create fields for linked feature class
FieldDescription waterPipeGlobalID = FieldDescription.CreateGlobalIDField();
FieldDescription pipeName = FieldDescription.CreateStringField("PipeName", 255);
// Create a list of water pipe field descriptions
List<FieldDescription> pipeFieldDescriptions = new List<FieldDescription> { waterPipeGlobalID, pipeName };
// Create a linked feature class description
FeatureClassDescription linkedFeatureClassDescription = new FeatureClassDescription("WaterPipe", pipeFieldDescriptions,
new ShapeDescription(GeometryType.Polygon, spatialReference));
// Add the creation of the linked feature class to the list of DDL tasks
FeatureClassToken linkedFeatureClassToken = schemaBuilder.Create(linkedFeatureClassDescription);
// Create a feature-linked annotation feature class description object
AnnotationFeatureClassDescription annotationFeatureClassDescription =new AnnotationFeatureClassDescription(annotationFeatureClassName,
fieldDescriptions, shapeDescription, maplexGeneralPlacementProperties, labelClasses, new FeatureClassDescription(linkedFeatureClassToken))
{
IsAutoCreate = true,
IsSymbolIDRequired = false,
IsUpdatedOnShapeChange = true
};
The SchemaBuilder.Modify
method modifies a annotation feature class following a similar pattern as modifying a feature class. The following example illustrates adding labels and symbols in an annotation feature class.
// Modifying annotation feature class' labels and symbols
// Get original annotation feature class description
AnnotationFeatureClassDescription annotationFeatureClassDescription = new AnnotationFeatureClassDescription(annotationFeatureClassDefinition);
// Get original label classes
IReadOnlyList<CIMLabelClass> labelClasses = annotationFeatureClassDescription.LabelClasses;
List<CIMLabelClass> modifiedLabelClasses = new List<CIMLabelClass>(labelClasses);
// Add a new label class
modifiedLabelClasses.Add(new CIMLabelClass()
{
Name = "RedSymbol",
TextSymbol = new CIMSymbolReference
{
Symbol = new CIMTextSymbol()
{
Angle = 45,
FontType = FontType.Type1,
FontFamilyName = "Arial",
FontEffects = FontEffects.Normal,
HaloSize = 2.0,
Symbol = new CIMPolygonSymbol { SymbolLayers = new CIMSymbolLayer[]
{ new CIMSolidFill { Color = CIMColor.CreateRGBColor(255, 0, 0) } }, UseRealWorldSymbolSizes = true }
},
MaxScale = 0,
MinScale = 0,
SymbolName = "TextSymbol-RED"
},
});
// Add a new symbol
annotationFeatureClassDescription.Symbols.Add(new CIMSymbolIdentifier()
{
ID = 1001,
Name = "ID_10001",
Symbol = new CIMTextSymbol()
{
Angle = 43,
FontEffects = FontEffects.Subscript,
FontType = FontType.TTOpenType,
FontStyleName = "Regular",
FontFamilyName = "Tahoma",
TextCase = TextCase.Allcaps
}
});
// Create modified description object
AnnotationFeatureClassDescription modifiedAnnotationFeatureClassDescription = new AnnotationFeatureClassDescription(annotationFeatureClassDescription.Name,
annotationFeatureClassDescription.FieldDescriptions, annotationFeatureClassDescription.ShapeDescription,
annotationFeatureClassDescription.GeneralPlacementProperties, modifiedLabelClasses);
// Enqueue DDL modification
schemaBuilder.Modify(modifiedAnnotationFeatureClassDescription);
// Execute DDL operations
schemaBuilder.Build();
Following a feature class rename pattern above, an annotation feature class can be renamed using SchemaBuilder.Rename
.
Like tables and feature classes, annotation feature classes can be deleted using the same DDL delete operation pattern. First, create an AnnotationFeatureClassDescription
object and then call SchemaBuilder.Delete
:
// Create a SchemaBuilder object
SchemaBuilder schemaBuilder = new SchemaBuilder(geodatabase);
// Createa an AnnotationFeatureDescription object
AnnotationFeatureClassDescription annotationFeatureClassDescription = new AnnotationFeatureClassDescription(annotationFeatureClass.GetDefinition());
//Add the deletion of annotation feature class to the list of DDL tasks
schemaBuilder.Delete(annotationFeatureClassDescription);
// Execute the DDL
schemaBuilder.Build();
The Pro SDK DDL APIs support the creation of subtypes in a dataset. Subtypes are used as a method to categorize data. They are a subset of features in a feature class or objects in a table that share the same attributes.
To enable support for subtypes in a table or feature class, the SubtypeFieldDescription
property of the TableDescription
or FeatureClassDescription
should be set and passed to the schema builder method.
The SubtypeFieldDescription
class defines the subtype field name and the subtype's code/value pair. The snippets below show how to get started with subtypes.
First, create an integer field to hold subtypes.
// Create an integer field named "BuildingType."
FieldDescription buildingType = new FieldDescription("BuildingType", FieldType.Integer);
// Additional field to hold name (optional)
FieldDescription buildingName = new FieldDescription("Name", FieldType.String);
Next, create the subtype field description that defines the subtype field name and the subtypes for a dataset. You should utilize an existing numeric data type field (integer or double) as a subtype field.
// Create 'BuildingType' as a subtype field with three subtypes - Business, Marketing, and Security
SubtypeFieldDescription buildingSubtypeDescription = new SubtypeFieldDescription(buildingType.Name, new Dictionary<int, string> { { 1, "Business" }, { 2, "Marketing" }, { 3, "Security" } })
Then, create the table or feature class description object, set the SubtypeFieldDescription
property, and pass it to the SchemaBuilder.Create
method.
// Creating a table with two fields
TableDescription tableDescription = new TableDescription("Building", new List<FieldDescription> { buildingName, buildingType });
// Set subtype field
tableDescription.SubtypeFieldDescription = subtypeFieldDescription;
// Enqueue table with subtype creation
schemaBuilder.Create(tableDescription);
// Execute the DDL
schemaBuilder.Build();
The DDL APIs also support modifying subtypes using SchemaBuilder.Modify
. The subtype modification allows you to add or remove subtypes and alter the default subtype code.
TableDescription tableDescription = new TableDescription(tableDefinition);
// Remove the first subtype from the table
IReadOnlyList<Subtype> subtypes = tableDefinition.GetSubtypes();
tableDescription.SubtypeFieldDescription.Subtypes.Remove(subtypes.First().GetCode());
// Adding a new subtype, 'Utility', in the existing table
tableDescription.SubtypeFieldDescription.Subtypes.Add(4, "Utility");
// Assigning 'Utility' subtype as the default subtype
tableDescription.SubtypeFieldDescription.DefaultSubtypeCode = 4;
// Enqueue subtype modification
schemaBuilder.Modify(tableDescription);
schemaBuilder.Build();
Disabling subtype support for a feature class or table will remove all subtype values and eliminates the subtype designation of the previous subtype field.
To disable subtype support, you must set the SubtypeFieldDescription
property of TableDescription
or FeatureClassDescription
to null
and pass the modified description object to the SchemaBuilder.Modify
method.
FeatureClassDescription featureClassDescription = new FeatureClassDescription(featureClassDefinition);
// Set subtype field to null to remove the subtype field designation
featureClassDescription.SubtypeFieldDescription = null;
schemaBuilder.Modify(featureClassDescription);
schemaBuilder.Build();
Relationship classes create an association between fields or features in the origin and destination classes.
The creation of a relationship class begins with an origin and a destination class. In the DDL, the RelationshipClassDescription
class defines the relationship between classes.
Following the similar DDL creation pattern, a RelationshipClassDescription
object is passed to SchemaBuilder.Create
method, and then SchemaBuilder.Build
is used to create a relationship class. The following example illustrates creating a relationship class between a 'Building' feature class and a 'BuildingType' table.
// Creating the 'BuildingType' table with two fields - BuildingType and BuildingTypeDescription
FieldDescription buildingType = FieldDescription.CreateIntegerField("BuildingType");
FieldDescription buildingTypeDescription = FieldDescription.CreateStringField("BuildingTypeDescription", 100);
TableDescription buildingTableDescription = new TableDescription("BuildingType", new List<FieldDescription>() { buildingType, buildingTypeDescription });
TableToken buildingTypeTableToken = schemaBuilder.Create(buildingTableDescription);
// Creating the 'Building' feature class with three fields - BuildingId, Address, and BuildingType
FieldDescription buildingId = FieldDescription.CreateIntegerField("BuildingId");
FieldDescription buildingAddress = FieldDescription.CreateStringField("Address", 100);
FieldDescription usageSubType = FieldDescription.CreateIntegerField("UsageSubtype");
FeatureClassDescription featureClassDescription = new FeatureClassDescription("Building", new List<FieldDescription> { buildingId, buildingAddress, buildingType, usageSubType},
new ShapeDescription(GeometryType.Polygon, SpatialReferences.WGS84));
// Adding two subtypes- Marketing and Utility
featureClassDescription.SubtypeFieldDescription = new SubtypeFieldDescription(usageSubType.Name, new Dictionary<int, string> {{1,"Marketing"},{2,"Utility"}});
FeatureClassToken buildingToken = schemaBuilder.Create(featureClassDescription);
// Creating a 1:M, composite relationship between the 'Building' feature class and 'BuildingType' table
RelationshipClassDescription relationshipClassDescription = new RelationshipClassDescription("BuildingToBuildingType", new FeatureClassDescription(buildingToken),
new TableDescription(buildingTypeTableToken), RelationshipCardinality.OneToMany, buildingType.Name, buildingType.Name)
{
RelationshipType = RelationshipType.Composite
};
A set of relationship rules can be added to a relationship class to refine the cardinality and relationship combinations. The RelationshipRuleDescription
class defines a relationship rule. If a subtype exists in the dataset, the relationship rule description constructor takes the subtype code of the origin or the destination class; otherwise, it should be null.
// Adding a relationship rule for the building usage subtype ( Marketing)
relationshipClassDescription.RelationshipRuleDescriptions.Add(new RelationshipRuleDescription(1, null));
Finally, call the schema builder's create and build methods to enqueue and execute the DDL operations respectively.
schemaBuilder.Create(relationshipClassDescription);
schemaBuilder.Build();
Many-to-many (M:M) relationship classes require the relationship class to have its own table in the database. Therefore, you should use
AttributedRelationshipClassDescription
to create a many-to-many relationship.
Creating an attributed relationship class follows many of the same principles as building a relationship class. The critical difference is that you must provide the destination primary and destination foreign keys.
The AttributedRelationshipClassDescription
is used to create an attributed relationship class as below.
// Creating the 'BuildingType' table with two fields - BuildingType and BuildingTypeDescription
FieldDescription buildingType = FieldDescription.CreateIntegerField("BuildingType");
FieldDescription buildingTypeDescription = FieldDescription.CreateStringField("BuildingTypeDescription", 100);
TableDescription buildingTableDescription = new TableDescription("BuildingType", new List<FieldDescription>() { buildingType, buildingTypeDescription });
TableToken buildingTypeTableToken = schemaBuilder.Create(buildingTableDescription);
// Creating the 'Building' feature class with three fields - BuildingId, Address, and BuildingType
FieldDescription buildingId = FieldDescription.CreateIntegerField("BuildingId");
FieldDescription buildingAddress = FieldDescription.CreateStringField("Address", 100);
FeatureClassDescription featureClassDescription = new FeatureClassDescription("Building", new List<FieldDescription> { buildingId, buildingAddress, buildingType }, new ShapeDescription(GeometryType.Polygon, SpatialReferences.WGS84));
FeatureClassToken buildingToken = schemaBuilder.Create(featureClassDescription);
// Creating M:M relationship between the 'Building' feature class and 'BuildingType' table
AttributedRelationshipClassDescription attributedRelationshipClassDescription =
new AttributedRelationshipClassDescription("BuildingToBuildingType", new FeatureClassDescription(buildingToken),
new TableDescription(buildingTypeTableToken), RelationshipCardinality.ManyToMany, "OBJECTID", "BuildingID", "OBJECTID", "BuildingTypeID");
// Adding attribute field in the relationship table - 'OwnershipPercentage' field (optional)
attributedRelationshipClassDescription.FieldDescriptions.Add(FieldDescription.CreateIntegerField("OwnershipPercentage"));
schemaBuilder.Create(attributedRelationshipClassDescription);
schemaBuilder.Build();
An attributed relationship table can contain any user defined attributes.
The relationship table must have fields that act as foreign keys to the origin and destination class. These foreign keys relate to the primary keys on the origin and destination classes.
Once a relationship class is created, it cannot be modified. However, you can add, delete, and refine its rules and update the split policy using SchemaBuilder.Modify
.
For an attributed relationship class, you can add or remove attribute fields from the intermediate relationship table; see the code snippet below.
AttributedRelationshipClassDescription attributedRelationshipClassDescription = new AttributedRelationshipClassDescription(attributedRelationshipClassDefinition);
// Update relationship split policy
attributedRelationshipClassDescription.RelationshipSplitPolicy = RelationshipSplitPolicy.UseDefault;
// Add a field in the relationship table
attributedRelationshipClassDescription.FieldDescriptions.Add(FieldDescription.CreateIntegerField("RelationshipStatus"));
// Add relationship rules based on subtypes,if available assuming origin class has subtype with code 1
attributedRelationshipClassDescription.RelationshipRuleDescriptions.Add(new RelationshipRuleDescription(1, null));
schemaBuilder.Modify(attributedRelationshipClassDescription);
schemaBuilder.Build();
The SDK APIs can delete relationship classes by passing the relationship description object to the SchemaBuilder.Delete
method, as shown below.
// Relationship description from existing dataset
RelationshipClassDescription relationshipClassDescription = new RelationshipClassDescription(relationshipClassDefinition);
schemaBuilder.Delete(relationshipClassDescription);
schemaBuilder.Build();
Similar to feature classes, relationship classes also can be moved in and out of a feature dataset using SchemaBuilder.AddRelationshipClass
and SchemaBuilder.RemoveRelationshipClass
respectively. See the following code snippet for details.
// Get existing feature dataset and relationship class
FeatureDatasetDescription featureDatasetDescription = new FeatureDatasetDescription(featureDatasetDefinition);
RelationshipClassDescription relationshipClassDescription = new RelationshipClassDescription(relationshipClassDefinition);
// Remove relationship class from the feature dataset
schemaBuilder.RemoveRelationshipClass(featureDatasetDescription, relationshipClassDescription);
// Add relationship class in to the feature dataset
//schemaBuilder.AddRelationshipClass(featureDatasetDescription, relationshipClassDescription);
schemaBuilder.Build();
The DDL APIs also support creating and removing of both spatial and attribute indexes. The AttributeIndexDescription
and SpatialIndexDescription
classes define attribute and spatial indexes, respectively. The index creation or deletion process follows similar DDL creation and deletion patterns as other DDL operations.
To create an attribute index description object, the AttributeIndexDescription
constructor takes the index name, the table dataset where the index is to be built, and a set of table fields participating in the index. The AttributeIndexDescription
object is then passed to the SchemaBuilder.Create
method, as illustrated below.
// Creating a table with index from scratch
// Table fields
FieldDescription nameFieldDescription = FieldDescription.CreateStringField("Name", 50);
FieldDescription addressFieldDescription = FieldDescription.CreateStringField("Address", 200);
// Creating a table, 'Buildings' with two fields
TableDescription tableDescription = new TableDescription("Buildings", new List<FieldDescription>() { nameFieldDescription, addressFieldDescription });
// Enqueue DDL operation to create a table
TableToken tableToken = schemaBuilder.Create(tableDescription);
// Creating an attribute index named as 'Idx' with two participating fields
AttributeIndexDescription attributeIndexDescription = new AttributeIndexDescription("Idx", new TableDescription(tableToken),
new List<string> { nameFieldDescription.Name, addressFieldDescription.Name });
// Enqueue DDL operation to create attribute index
schemaBuilder.Create(attributeIndexDescription);
// Execute build indexes operation
bool isBuildSuccess = schemaBuilder.Build();
The SpatialIndexDescription
constructor takes a feature class description to create a spatial index description object. Then you can call SchemaBuilder.Create
and SchemaBuilder.Build
to build the spatial index, as shown below.
// Creating the spatial index description object
SpatialIndexDescription spatialIndexDescription = new SpatialIndexDescription(new FeatureClassDescription(featureClassDefinition));
// Enqueue DDL operation for spatial index creation
schemaBuilder.Create(spatialIndexDescription);
// Execute build index DDL operation
bool isBuildSuccess = schemaBuilder.Build();
This operation will recalculate the spatial index if a dataset already has the spatial index.
Following the DDL delete pattern, you can use SchemaBuilder.Delete
to delete indexes.
#region Removing attribute index
// Find attribute index to be removed
Index indexToRemove = featureClassDefinition.GetIndexes().First(f => f.GetName().Equals("IndexNameToBeRemoved"));
// Index description of the index to be removed
AttributeIndexDescription indexDescriptionToRemove = new AttributeIndexDescription(indexToRemove, new TableDescription(featureClassDefinition));
// Enqueue DDL operation to remove attribute index
schemaBuilder.Delete(indexDescriptionToRemove);
#endregion
# region Removing spatial index
// Create spatial description
SpatialIndexDescription spatialIndexDescription = new SpatialIndexDescription(new FeatureClassDescription(featureClassDefinition));
// Enqueue DDL operation to remove spaital index
schemaBuilder.Delete(spatialIndexDescription);
# endregion
// Execute delete indexes operation
bool isDeleteIndexSuccess = schemaBuilder.Build();
Creating and deleting file geodatabases follows a slightly different pattern than other DDL operations. Static routines on the SchemaBuilder
class called CreateGeodatabase
and DeleteGeodatabase
are used to create and delete file geodatabases. Both routines take a FileGeodatabaseConnectionPath
as an argument.
If a file geodatabase is in use it cannot be deleted. All geodatabase objects that refer to the file geodatabase, or datasets or rows within it, must be disposed prior to deletion.
Creating and deleting mobile geodatabases follows a similar pattern as creating and deleting file geodatabases. Static routines on the SchemaBuilder
class called CreateGeodatabase
and DeleteGeodatabase
create and delete mobile geodatabases. Both routines take a MobileGeodatabaseConnectionPath
as an argument.
Like a file geodatabase, a mobile geodatabase cannot be deleted if it is in use. All geodatabase objects that refer to the mobile geodatabase, or datasets or rows within it, must be disposed prior to deletion.
ArcGIS Pro supports a concept of memory geodatabases. Memory geodatabases are often significantly faster than writing to on-disk formats. Data written into memory is temporary and is deleted when the application is closed. Memory geodatabases are stored in your systemβs physical memory, and your system may run low on memory if you write large datasets into the workspace.
Geoprocessing supports two kinds of memory geodatabases. The Pro SDK only supports the
memory
variant, not the legacyin_memory
workspace.
To create a memory geodatabase, first create a MemoryConnectionProperties
object. There are two constructors for this class. The default constructor returns a pointer to the same memory
geodatabase used by geoprocessing. The second constructor takes a string. Passing in βmemoryβ also returns a pointer to the geoprocessing memory
workspace. Passing in another value creates a unique memory geodatabase with the specified name. (The string βin_memoryβ generates an error, to avoid any possible confusion with the unsupported in_memory
workspace.)
Once the MemoryConnectionProperties
object is created, static routines on the SchemaBuilder
class, CreateGeodatabase
and DeleteGeodatabase
, can be used to create and delete the memory geodatabase.
After the geodatabase is created, tables and feature classes can be created inside it using the regular DDL routines.
The code below shows an example of creating a memory geodatabase called "MyMemoryGeodatabase":
// Create the memory connection properties to connect to the memory geodatabase named as 'MyMemoryGeodatabase'
MemoryConnectionProperties memoryConnectionProperties = new MemoryConnectionProperties("MyMemoryGeodatabase");
// Create and use the memory geodatabase
using (Geodatabase geodatabase = new Geodatabase(memoryConnectionProperties))
{
// Create additional schema here
}