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

In this topic


Introduction

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.

General Usage

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.

Tokens

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.

Tables and Feature Classes

Creating Tables and Feature Classes

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();

Modifying and Renaming Tables and Feature Classes

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();

Adding or Removing Fields

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();

Modifying Existing Fields

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();

Deleting Tables and Feature Classes

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();

Domains

Both coded value and range domains can be created with the DDL APIs in the Pro SDK.

Creating Domains

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();

Modifying and Renaming Domains

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

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.

Annotation

Creating Annotation Feature Classes

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
}; 

Modifying and Renaming Annotation Feature Classes

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.

Deleting Annotation Feature Classes

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();

Subtypes

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.

Enabling Subtype Support

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();

Modifying Subtypes

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

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();

Relationships

Relationship classes create an association between fields or features in the origin and destination classes.

Creating Relationship 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 Attributed Relationship Classes

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.

Modifying Relationship 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();

Deleting Relationship Classes

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();

Indexes

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.

Creating Attribute Indexes

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();

Creating Spatial Indexes

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.

Deleting Indexes

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();

Geodatabases

File Geodatabases

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.

Mobile Geodatabases

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.

Memory Geodatabases

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 legacy in_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
}
⚠️ **GitHub.com Fallback** ⚠️