Geodatabase


Supported with:
Library dependencies: System, SystemUI, Geometry, Display, Server, Output

Additional library information: Contents, Object Model Diagram

The Geodatabase library provides the application programming interface (API) for the geodatabase. The geodatabase is a repository of geographic data built on standard industry relational and object relational database technology. The objects in the library provide a unified programming model for all supported data sources in ArcGIS. The Geodatabase library defines many of the interfaces that are implemented by data source providers higher in the architecture.
 
The geodatabase can be extended by developers to support specialized types of data objects (Features for example). In addition, it can have custom vector data sources added using the plug-in data source objects. The native data types supported by the geodatabase cannot be extended.

The objects that implement this functionality are grouped into a number of library subsystems. These library subsystems are:

Core geodatabase

The core geodatabase objects are detailed in the following diagram:
 
 
 
The diagram shown above is a simplified view of the most important geodatabase objects, which are summarized as follows:
Workspaces
The Workspace object is a container of spatial and non-spatial datasets such as feature classes, raster datasets, and tables. It provides methods to instantiate existing datasets and to create new datasets. The following diagram details the workspace objects.
 
 
Workspaces are classified into types specified by the esriWorkspaceType enumerator: esriFileSystemWorkspace, esriLocalDatabaseWorkspace, and esriRemoteDatabaseWorkspace.
 
Shapefiles and ArcInfo workspaces are examples of esriFileSystemWorkspace. A personal geodatabase stored in Microsoft Access or a file geodatabase is an example of esriLocalDatabaseWorkspace. An enterprise geodatabase stored in a relational database management system (RDBMS), such as Oracle, DB2, SQL Server, or Informix, and accessed via ArcSDE is an example of esriRemoteDatabaseWorkspace.
 
Other workspace types include the following:
 
WorkspaceFactory is a dispenser of workspaces and allows a client to connect to a workspace specified by a set of connection properties. A workspace represents a database or a data source that contains one or more datasets. Examples of datasets include tables, feature classes, and relationship classes. Various WorkspaceFactory types are shown in the following illustration.
 
 
WorkspaceFactory is a cocreatable, singleton object. WorkspaceFactory maintains a pool of currently connected, active workspaces that are referenced by the application. Connection properties are specified using a PropertySet object and can be saved to a connection file.
 
WorkspaceFactory also supports methods that can be used to browse and manage file system workspaces and methods to manage connection files for remote database workspaces.
 
The following code example connects to an ArcSDE for Oracle geodatabase.

[C#]
ESRI.ArcGIS.esriSystem.IPropertySet propertySet = new
  ESRI.ArcGIS.esriSystem.PropertySetClass();

propertySet.SetProperty("SERVER", "cuillin");
propertySet.SetProperty("INSTANCE", "esri_sde");
propertySet.SetProperty("USER", "scott");
propertySet.SetProperty("PASSWORD", "tiger");
propertySet.SetProperty("VERSION", "SDE.DEFAULT");
propertySet.SetProperty("AUTHENTICATION_MODE", "DBMS");

IWorkspaceFactory workspaceFactory = new
  ESRI.ArcGIS.DataSourcesGDB.SdeWorkspaceFactoryClass();
IWorkspace workspace = workspaceFactory.Open(propertySet, 0);
The IFeatureWorkspace interface is used to access and manage datasets that are a key component of a feature-based geodatabase: Tables, ObjectClasses, FeatureClasses, FeatureDatasets, and RelationshipClasses.
 
All of the Open methods, such as OpenTable, take a dataset name as input. When working with an ArcSDE geodatabase, the name may be fully qualified (for example, database.owner.tablename or owner.tablename) using the qualification character appropriate to the underlying database (see ISQLSyntax). If the input name is not fully qualified, then it is qualified using the currently connected user for the workspace. When working with geodatabases (personal, file, or ArcSDE), the workspace keeps a running object table of instantiated datasets. Multiple calls to open an already instantiated dataset will return a reference to the already instantiated dataset.
 
The OpenTable method can be used to open any existing table or object class in the workspace given its fully qualified name. The table object returned will always support the ITable interface. The returned table object will support additional interfaces depending on the type of table—for example, ObjectClasses will additionally support the IObjectClass interface.
 
The OpenFeatureClass method can be used to open any existing feature class in the workspace given its fully qualified name. Every feature class in a geodatabase has a unique fully qualified name, and the OpenFeatureClass method can be used to directly open FeatureClasses that are part of a FeatureDataset.
 
The following code example opens a shapefile as a feature class.

[C#]
public void OpenFeatureClass_Example()
{
  IWorkspaceFactory workspaceFactory = new
    ESRI.ArcGIS.DataSourcesFile.ShapefileWorkspaceFactoryClass();

  IWorkspace workspace = workspaceFactory.OpenFromFile(@"D:\Data\Esridata\USA",
    0);

  //Cast to IFeatureWorkspace. 
  IFeatureWorkspace featureWorkspace = (IFeatureWorkspace)workspace;

  IFeatureClass featureClass = featureWorkspace.OpenFeatureClass("States");
  System.Windows.Forms.MessageBox.Show("There are " + featureClass.FeatureCount
    (null) + " states");
}
Applications can use the ISQLSyntax interface to help them construct Structured Query Language (SQL) queries and where clauses that are database-system independent.
 
GetSpecialCharacter can be used to return the database management system (DBMS)-dependent character that represents esriSQLSpecialCharacters, including the following:
 
Applications should use the ParseTableName and ParseColumnName methods to split the fully qualified name for a dataset or for a column in a table into its components (database, owner, table, column). Applications that are to be RDBMS independent should not assume that “.” is the delimiter used to separate the components of a fully qualified dataset name. Both the IDataset.Name property for a dataset in a geodatabase and the IDatasetName.Name property for a dataset name object return the fully qualified name for the dataset (the name object for the dataset, obtained using the IDataset.FullName property).
 
Applications should use the QualifyTableName and QualifyColumnName methods to construct fully qualified dataset and column names.
 
Editing data
Applications can use specified transactions to manage direct updates, for example, any feature class that is tagged as not requiring an edit session can be updated outside an edit session. When using transactions to manage direct updates, applications are responsible for discarding any cached row objects at transaction boundaries.
 
ITransactions is an optional interface that allows an application to explicitly control database transactions. The interface does not support nested transactions. Applications should not use transactions when performing updates within an edit session. In the context of an edit session, transactions are managed by the workspace and automatically started and stopped as needed.
 
Applications should be aware that Data Definition Language (DDL) operations made through the ArcObjects geodatabase data access objects (for example, deleting a feature dataset or creating a new feature class) use database transactions to ensure integrity of the data dictionary tables and commit the transaction at the end of the operation. Applications should not invoke DDL operations within an application transaction—application transactions should be restricted to Data Manipulation Language (DML) operations (such as data updates).
 
Applications can use geodatabase edit sessions to manage database transactions. It is possible to start an edit session in one of two modes. The IWorkspaceEdit interface allows the application to start and stop edit sessions in the versioned edit session mode only.
 
A versioned edit session is begun using the StartEditing method. The withUndoRedo parameter can be used to suppress undo/redo logging if the workspace supports such suppression. Shapefiles support suppression of Undo/Redo logging, but ArcSDE does not.
 
The IMultiuserWorkspaceEdit interface also allows the application to start and stop edit sessions during which the objects in a geodatabase can be updated. This differs from the IWorkspaceEdit interface in that the IMultiuserWorkspaceEdit interface is only supported by workspaces that support both edit session modes, namely ArcSDE geodatabases. For this reason, it is recommended that the IMultiuserWorkspaceEdit interface be used only if it is the intent to control the edit session mode used when editing. By default, through the StartEditing method, an edit session will be started in the versioned edit session mode. Therefore at this time it is necessary to use the IWorkspaceEdit interface to edit local workspaces.
 
If the IMultiuserWorkspaceEdit interface is used, the edit session can be started in either versioned or non versioned edit session modes. If the edit session mode is set to versioned, the only changes to data that an application sees within an edit session are changes that are made by that application. Changes made by other concurrently executing applications (if allowed) are not visible until the edit session is saved or discarded. On the other hand, editing within an edit session in non versioned mode is the equivalent of performing standard database transactions. You still perform the edits within a standard edit session; however, when you’ve finished, the changes are committed as a single transaction by saving. If you don’t want to commit the changes, you abort the edit session without saving. Each transaction can include as few or as many operations as required, provided they fall within a
single edit session.
 
Editing in a non versioned edit session mode alters the data source directly, it doesn’t store the changes in delta tables as versioned editing does. This avoids the overhead of managing these extra tables and allows you to easily adapt non-ESRI applications so that they can read and edit the data. The drawback is that since you edit the data source directly, you cannot undo or redo an individual edit if you make a mistake. The only way to undo edits is to undo all edits by quitting the edit session without saving. You can perform non versioned edits on simple data only: points, lines, polygons, annotation, and relationships. You cannot perform non versioned edits on complex data such as feature classes in a topology or geometric network.
 
If undo/redo facilities are required when editing in a versioned edit session mode, all related changes to objects in the database within an edit session should be grouped into edit operations. An edit operation is begun using the StartEditOperation method. Applications are responsible for calling the AbortEditOperation method to abort an edit operation if errors are detected within the methods executed for an edit operation. Applications are responsible for calling StopEditOperation to mark the end of a successfully completed edit operation. Completed edit operations can be thought of as being pushed onto an undo stack.
 
The UndoEditOperation method can be used to roll the state of the edit session back to what it was prior to the execution of the edit operation at the top of the undo stack. Undoing an edit operation pops the edit operation from the undo stack and adds it to a redo stack. The RedoEditOperation method rolls the state of the edit session forward to what it was after the execution of the edit operation at the top of the redo stack, pops the redone edit operation from the redo stack, and pushes it back onto the undo stack. Performing a new edit operation clears the redo stack.
 
The StopEditing method is used to end an edit session. The saveEdits parameter controls whether or not edits are saved or discarded. A multiversioned database can support multiple concurrent edit sessions on the same version of the database. In such a scenario, StopEditing will return an error code of FDO_E_VERSION_REDEFINED if it detects that the database state associated with the version being edited is no longer the same as it was at the beginning of the edit session (indicating that the version was modified by some other edit session). In this case, the application is responsible for calling the IVersionEdit.Reconcile method to reconcile the edit session against the current state of the version being edited. StopEditing may be called again after reconciliation.
 
The geodatabase guarantees “unique instancing” of row objects retrieved from the database within an edit session. Any data access call that retrieves a nonrecycling object with a particular object ID will return the in-memory instance of the object if the object has already been instantiated by the application. Such behavior is needed to ensure application correctness when updating complex object models—for example, models with relationship-based messaging or models with network features where updates to the geometry of a feature affect the geometry of topologically related features.
 
The code example below shows a simple non versioned edit session on an ArcSDE workspace. Note that if you undo the edit operation, there will be no outstanding edits, so the prompt to save the work will not appear. For this reason, all object editing should be done within an edit session. The geodatabase data access APIs (such as IRow.Store, ITable.Update, and ITable.Insert) will fail if you attempt to use them outside an edit session on object and feature classes that are marked as requiring an edit session to ensure unique instancing semantics. Use IObjectClassInfo2.CanBypassEditSession to determine the situation.

[C#]
public void SDE_WorkspaceEdit()
{
  ESRI.ArcGIS.esriSystem.IPropertySet propertySet = new
    ESRI.ArcGIS.esriSystem.PropertySetClass();

  propertySet.SetProperty("SERVER", "bigsky");
  propertySet.SetProperty("INSTANCE", "5151");
  propertySet.SetProperty("USER", "gdb");
  propertySet.SetProperty("PASSWORD", "gdb");
  propertySet.SetProperty("VERSION", "SDE.DEFAULT");

  IWorkspaceFactory workspaceFactory = new
    ESRI.ArcGIS.DataSourcesGDB.SdeWorkspaceFactoryClass();
  IWorkspace workspace = workspaceFactory.Open(propertySet, 0);

  //Cast to IFeatureWorkspace. 
  IFeatureWorkspace featureWorkspace = (IFeatureWorkspace)workspace;

  IFeatureClass featureClass = featureWorkspace.OpenFeatureClass("States");

  IFeature feature;

  IWorkspaceEdit workspaceEdit = (IWorkspaceEdit)featureWorkspace;
  IMultiuserWorkspaceEdit muWorkspaceEdit = (IMultiuserWorkspaceEdit)
    featureWorkspace;

  if (muWorkspaceEdit.SupportsMultiuserEditSessionMode
    (esriMultiuserEditSessionMode.esriMESMNonVersioned))
  {
    muWorkspaceEdit.StartMultiuserEditing
      (esriMultiuserEditSessionMode.esriMESMNonVersioned);
    workspaceEdit.StartEditOperation();
    feature = featureClass.GetFeature(1);
    feature.Delete();
    bool hasEdits = false;
    workspaceEdit.HasEdits(ref hasEdits);
    workspaceEdit.StopEditOperation();

    System.Windows.Forms.DialogResult result =
      System.Windows.Forms.MessageBox.Show("Save Edits?", "Save Edits Dialog",
      System.Windows.Forms.MessageBoxButtons.YesNo,
      System.Windows.Forms.MessageBoxIcon.Question);
    If(result == System.Windows.Forms.DialogResult.Yes && hasEdits)
    {
      workspaceEdit.StopEditing(true);
    }
    else
    {
      workspaceEdit.StopEditing(false);
    }
  }
}
The rules for correct object editing on a geodatabase are summarized as follows:
Workspace extensions
A workspace representing a geodatabase can have one or more workspace extensions. A workspace extension extends the functionality of a workspace in some way, for example, by managing a new type of custom dataset or by maintaining custom data dictionary information on datasets. A workspace extension is usually used in conjunction with an application or editor extension that acts as the client of the workspace extension.
 
The workspace instantiates all workspace extensions that are registered in the component category CATIDs.GeodatabaseWorkspaceExtensions at connect time. An application extension can find a workspace extension by its well-known globally unique identifier (GUID) and invoke methods supported by the extension as appropriate. It will also instantiate all workspace extensions registered with a workspace using IWorkspaceExtensionManager.RegisterExtension.
 
IWorkspaceExtension is a mandatory interface that must be supported by all workspace extensions. The GUID property returns the well-known GUID for the extension and is guaranteed to be unique. The Name property is the name of the extension. The PrivateDatasetNames and DataDictionaryTableNames properties return the names of tables and datasets that are private to the extension and will not be exposed to browsing clients by the workspace.
 
Datasets
Dataset is an abstract class that represents a named collection of data in a workspace. Datasets can contain other datasets. All datasets support the IDataset interface and may optionally support other interfaces, including IDatasetEdit, ISchemaLock, and IMetadata.
 
The following diagram details the dataset objects.
 
 
Examples of datasets include tables, feature classes, relationship classes, feature datasets, topologies, and geometric networks. Datasets appear as items in the ArcCatalog treeview under their workspace as shown in the following screen shot:
 
 
Feature datasets are collections of feature classes and can also contain relationship classes, geometric networks, or topologies. Feature classes that store simple features can be organized either inside or outside a feature dataset. Those outside a feature dataset are called standalone feature classes. Feature classes that store topological features, for example, those participating in geometric networks or topologies, must be contained within a feature dataset to ensure a common spatial reference. A FeatureDataset is a dataset that exists only in a geodatabase workspace; all the datasets contained in the FeatureDataset are also part of the same geodatabase.
 
The FeatureDataset object is shown in the following diagram.
 
 
Each dataset in a geodatabase must have a unique name. In particular, each feature class in a geodatabase must have a unique name independent of the feature dataset that contains it. This is different from a file system model, where two folders may contain files with the same local name within the folder.
 
When programming with feature classes, remember that the feature class may or may not belong to a feature dataset.
 
The following sample code shows how to get the workspace for a feature class and assumes a feature dataset exists and therefore may fail.

[C#]
// This excerpt won't work for standalone feature classes.
IFeatureDataset featureDataset = featureClass.FeatureDataset;
IWorkspace workspace = featureDataset.Workspace;
The following sample code will work for both standalone feature classes and those in feature datasets.
[C#]
IDataset dataset = (IDataset)featureClass;
IWorkspace workspace = dataset.Workspace;
Table, ObjectClass, and FeatureClass objects
Table, ObjectClass, and FeatureClass objects are detailed in the following diagram.
 
 
 
A Table object has one or more columns, referred to as fields, and contains an unordered collection of rows. For each field, each row has exactly one value in the data type of the field.
 
A table is a dataset; properties such as the name of the table, the persistable name object for the table, and the workspace containing the table can be obtained via the IDataset interface. In relational terms, a Table object represents an RDBMS table or view. In objected-oriented terms, a Table object represents an ObjectClass or a RelationshipClass in a geodatabase. A Table object hands out Row objects that support application-callable methods depending on the type of data stored in the table. The Name property of a Table, accessible via the IDataset interface, returns its fully qualified name. The level of qualification can vary depending on the host DBMS. For example, a table named "pipes" owned by a user named "gas" may be called "pipes" on Access, "gas.pipes" on Oracle, and "mydb.gas.pipes" on SQL Server. The ParseTableName method on the ISQLSyntax interface supported by the table’s workspace can be used to split the fully qualified name into its components.
 
The Table object is shown in the following diagram.
 
 
An ObjectClass object is a Table object whose rows represent entities, modeled as objects with properties and behaviors. The row objects handed out by ObjectClass support the IRow and the IObject interfaces.
 
ObjectClass can participate in any number of relationship classes (IObjectClass.RelationshipClasses) that relate its instances to objects (entities) in other object classes. ObjectClass can contain a discriminating field, referred to as the subtype field, that can be used to partition its instances into a number of subtypes. All subtypes share the same field definition and are stored in the same table; however, individual subtypes may differ in the default values and domains assigned to fields. The subtyping mechanism can also be used in defining attribute, connectivity and topology rules that apply to the instances of the object class. The subtyping mechanism is a lightweight alternative to creating multiple subclasses, and each is represented by its own ObjectClass.
 
ObjectClass has a nonnegative object class ID that is unique within the geodatabase. This ObjectClassID property is assigned to ObjectClass at the time it is created or at the time that an existing table in the RDBMS is registered with the geodatabase as an object class. The Name of the object class is the same as the name of the table in the DBMS in which the objects in the object class are stored; it follows the same fully qualified naming conventions.
 
ObjectClass can have an AliasName property that is stored as part of its definition by the geodatabase. AliasName can be retrieved and used for display purposes by applications.
 
The ObjectClass object is shown in the following diagram.
 
 
Although all objects in a FeatureClass or ObjectClass must have the same behavior and attributes, not all objects have to share the same default values and validation rules. You can group features and objects into subtypes. Subtypes differentiate objects based on their rules.
 
A FeatureClass is an ObjectClass whose objects are features, that is, a feature class is a collection of spatial entities, modeled as objects with properties and behaviors. All of the features in a feature class share the same attribute schema (they have the same set of named fields). The row objects handed out by a feature class support the IRow, IObject, and IFeature interfaces.
 
A feature class has one distinguished field of type Geometry, referred to as the shape field. The shape field stores the geometry (referred to as the ShapeType property) for the features in the FeatureClass.
 
The FeatureClass object is shown in the following diagram.
 
 
The following code example uses the Workspace object to return a FeatureClass object.

[C#]
IWorkspaceFactory workspaceFactory = new
  ESRI.ArcGIS.DataSourcesGDB.AccessWorkspaceFactoryClass();

IWorkspace workspace = workspaceFactory.OpenFromFile(@"C:\data\usa.mdb", 0);

//Cast to IFeatureWorkspace.
IFeatureWorkspace featureWorkspace = (IFeatureWorkspace)workspace;

IFeatureClass featureClass = featureWorkspace.OpenFeatureClass("States");
Row, Object, and Feature objects
The following diagram details the Row, Object, and Feature objects.
 
 
A RowBuffer object is a transient object that is capable of holding the state of a row but has no object identity. It is used primarily during data loading as the argument to the InsertRow method on an insert cursor. RowBuffer is obtained from a Table using the CreateRowBuffer method.
 
The IRowBuffer interface contains methods to access the state (the set of field values) for RowBuffer. These methods take as an argument the numeric index of the field to be accessed.
 
The RowBuffer and Row objects are shown in the following diagram.
 
 
A Row object is an instantiated software object that represents a persistent row in a Table. A Row object is normally obtained from a cursor on a table (for example, ICursor.NextRow) or fetched directly given its object ID (for example, ITable.GetRow).
 
Once retrieved, clients can query the Row object for additional interfaces and invoke methods on the Row object. The CLSID property of a Table determines the type of row object returned by the Table. A new persistent row object is created using the ITable.CreateRow method. The act of creating the row assigns it identity. Applications should use the CreateRow method to create new persistent row objects, as opposed to directly cocreating the Row objects. The latter will not create a row in the underlying persistent store.
 
A Row has a set of Fields. The set of Fields for a Row is the same as the set of Fields for its Table. In particular, the numeric index of a field in the Fields collection of its table is the same as the numeric index of the field in the Fields collection of the row, which is the same as the numeric index used to access the value of the field from the row. This means that application programs can and should cache field numeric indexes using the FindField method on the Table object, rather than invoking the FindField method once per row returned by a Cursor.
 
The following code sample shows the creation of a row, then an update, followed by the deletion of the row.

[C#]
//Assume a valid reference to an ITable object.
IRow row;
int i = 0;

i = table.FindField("Name");

//Insert row.
row = table.CreateRow();
row.set_Value(i, "Exploits");
row.Store();

//Update row.
row.set_Value(i, "Badger");
row.Store();

//Delete row.
row.Delete();
An Object object is a Table whose Row objects represent entities. The Row objects handed out by an Object object support the IRow and the IObject interfaces and are referred to simply as Objects. Another name for Object object in this context is Entity object.
 
The IObject interface is almost identical to IRow, from which it inherits. The only additional property is a direct link to the Object class.
 
The instances of an Object can be partitioned into a number of subtypes. The IRowSubtypes interface on an Object contains methods that allow determination and modification of the subtype to which an Object belongs and allow initialization or resetting of the field values of an Object to the default values defined for its subtype.
 
The Object object is shown in the following diagram.
 
 
It is important to note that when Objects are programmatically created via the CreateRow method on the ITable interface (or for features with the CreateFeature method on the IFeatureClass interface), the default subtype is not automatically set, nor are the default values initialized. When using ArcMap, these tasks are automatically performed. However, if you are programmatically creating an Object (or Feature) that has default values, the following Visual Basic (VB) code fragment indicates the proper sequence that should be followed.

[C#]
//Assume you have an IFeatureClass pointer.
IFeature feature = featureClass.CreateFeature();

//Get the default subtype code for the feature class.
int defaultSubtype;

ISubtypes subtypes = (ISubtypes)featureClass;
defaultSubtype = subtypes.DefaultSubtypeCode;

//Set the subtype and initialize the default values for the feature.
IRowSubtypes rowSubTypes = (IRowSubtypes)feature;

rowSubTypes.SubtypeCode = defaultSubtype;
rowSubTypes.InitDefaultValues();
A Feature object is a spatial Object. It is also a member of a feature class, being a row in the feature class table. A feature has an associated shape, the type of which is defined by the feature class. The possible shape objects are Point, Multipoint, Multipatch, Polyline, and Polygon—these are all objects in the Geometry object model.
 
The Feature object is shown in the following diagram.
 
 
Typically you will deal with simple features, but there are various special kinds as defined by the esriFeatureType enumeration. These include annotation, dimension, raster catalog, and various network features.
 
The IFeature interface extends IObject and IRow, from which it inherits. The additional facilities have to do with the shape of the feature. You can use the Shape property to get or set the shape. This is more convenient than using the Value property, since you don’t have to work out the index of the shape field.
 
Query, Cursor, and Selection objects
The Query, Cursor, and Selection objects are detailed in the following diagram.
 

Cursors
A Cursor object is a data-access object that can be used either to iterate over the set of rows in a table or to query or insert new rows into a table. There are three forms of Cursor, referred to as search, insert, and update cursors. Each of these types of cursors is returned by the corresponding method (Search, Insert, or Update) on a Table or FeatureClass object. The Search and Update methods take a QueryFilter as input, which can be used to restrict the set of rows returned.
 
A search cursor can be used to retrieve rows specified by a query filter; it supports a NextRow method. An update cursor can be used to positionally update and delete rows specified by a query filter; it supports the NextRow, UpdateRow, and DeleteRow methods. An insert cursor is used to insert rows into a table and supports the InsertRow method. All these methods are available in the single ICursor interface—it is your responsibility to make the calls appropriate to the type of cursor.
 
The NextRow method on a search or update cursor returns the next row in the result set to the application. The Row object returned is allocated and hydrated by the cursor, and a reference to it is handed to the application. To retrieve all rows in a result set containing N rows, the application must make N calls to NextRow. In VB, a call to NextRow after the last row in the result set has been retrieved returns Nothing. In C++, a call to NextRow after the last row in the result set has been retrieved returns a value of S_FALSE and sets the output row reference to 0.
 
Cursors are forward only; they do not support backing up and retrieving rows that have already been retrieved nor do they support making multiple passes over data. If an application needs to make multiple passes over the data, the application must reexecute the query that returned the cursor. If both executions of the query are made within the same edit session (or database transaction with the appropriate level of isolation), the application is guaranteed not to see any changes made to the data by other concurrently executing applications.
 
The following code example shows a simple cursor operation. It prints out the value of the first field for each row in a table.

[C#]
ICursor cursor = table.Search(null, false);

IRow row = cursor.NextRow();

while (row = !null)
{
  System.Windows.Forms.MessageBox.Show(row.Value(0));
  row = cursor.NextRow();
}
No data is fetched from the database until the NextRow method is called.
A Cursor has a recycling property that controls how it allocates row objects. Recycling cursors allocate a single row object and rehydrate it on each fetch. They can be used to optimize read-only access, for example, when drawing. You cannot maintain a reference on a row object returned by a recycling cursor across multiple calls to NextRow on the cursor. Row objects returned by a recycling cursor should not be modified. Nonrecycling cursors return a separate row object on each fetch. The objects returned by a nonrecycling cursor can be modified (setting the IRowBuffer.Value property or any other custom ancestor supported by Row) and stored with polymorphic behavior.The geodatabase guarantees unique instance semantics on nonrecycling row objects fetched during an edit session. If the row object to be retrieved by a call to NextRow has already been instantiated in the calling application, then a reference to the existing row object will be returned.
 
All Row objects retrieved from a Table using a Cursor logically contain the same ordered set of fields, and this set is the same as the ordered set of fields for the Cursor and the Table. In particular, the numeric index of a field in the Fields collection of the table is the same as the numeric index of the field in the Fields collection of the cursor, which is the same as the numeric index of the field for the row. So, the FindField method needs to be used only once per table or cursor. If the query filter used in generating a cursor does not include certain fields, then the resulting row objects will still logically contain these fields; however, they will not have hydrated values for these fields. If an application accesses these field values for the row, a variant of type empty (VT_EMPTY) will be returned. This value is different from the Null value (VT_NULL) that is returned when the value of a fetched field is null.
 
The UpdateRow method can be used to update the row at the current position of an update cursor (making a call to NextRow on a cursor returns a row and positions the cursor on that row). After fetching a Row object using NextRow, the application can modify the row as needed, then call UpdateRow, passing in the modified row. This is an alternative to calling Store on the retrieved row. Using a recycling update cursor can be faster than calling Store on the rows returned by a search cursor when performing direct updates outside an edit session on simple data. If the Row objects for the table are not simple (they don't have custom behavior or participate in composite relationships or relationships with notification), then calling UpdateRow on the cursor will generate a call to Store on the Row object to trigger the custom behavior, and there will be no performance gain.
 
The DeleteRow method can be used to delete the row at the current position of an Update cursor (that is, to delete the Row returned by the last call to NextRow on this cursor). After fetching the Row object using NextRow, the application should call DeleteRow on the cursor to delete the row. The application is responsible for discarding the deleted Row object. Using a recycling update cursor to delete rows can be faster then calling Delete on the rows returned by a Search cursor when performing direct updates outside an edit session on simple data. If the Row objects for the table are not simple (they don't have custom behavior or participate in composite relationships or relationships with notification), then calling DeleteRow on the cursor will generate a call to Delete on the Row object to trigger the custom behavior, and there will be no performance gain.
 
Insert cursors are used to bulk insert rows. Using an insert cursor offers significantly faster performance for data loading into simple tables and feature classes (tables whose CLSID is esriGeoDatabase.Row, esriGeoDatabase.Object, or esriGeoDatabase.Feature) than the alternative: making multiple calls to CreateRow on the table followed by calling Store on the created row.
 
Insert cursors on tables that contain non-simple objects internally use the CreateRow and Store methods to achieve polymorphism, and there is no difference in performance in these cases. The InsertRow method takes a RowBuffer as an argument. Applications obtain a RowBuffer using the CreateRowBuffer method on the Table object into which rows are to be inserted. Each call to InsertRow on the cursor creates a new row in the database whose initial values are set to the values in the input row buffer. The object ID for the created row is returned by the InsertRow method.
 
The UseBuffering method argument to the Insert method on a table returns an insert cursor that buffers rows on the clients and sends them to the server in batches for increased performance. The application is responsible for calling Flush on the insert cursor after all rows have been inserted. If a call to Flush is not made, the cursor will flush its buffers on destruction (when the application releases all references on the cursor). However, relying on the destructor to flush the insert cursor does not give the application the chance to detect errors that may arise on the call to flush (for example, if the tablespace [disk] for the Table in the underlying database fills up).
 
The FeatureCursor object is a kind of Cursor object. It performs in the same way, except it is based on a feature class rather than a generic table. The FeatureCursor object is shown in the following diagram.
 
 
The IFeatureCursor interface provides access to a set of features in a feature class. It operates in the same way as ICursor, although it does not inherit from that interface. This saves you from using query interface (QI) when dealing with features rather than rows.
 
There is a direct relationship between the methods on the various interfaces of Cursor objects and feature cursors as shown in the following graphic.
 
 
Query filters and spatial filters
A QueryFilter object specifies a filter for tabular data based on attribute values. It is used to restrict the set of rows or the set of columns retrieved from a single table or feature class. The primary use of a query filter is to specify the set of rows to be returned when opening a cursor on a Table. It is also used in a number of other cases where a subset of the data in a table needs to be specified.
 
Some scenarios of using QueryFilter include opening a cursor on some of the rows in a table, selecting features in ArcMap, deleting some features meeting certain criteria, counting the number of features satisfying a condition, and defining which features will be rendered on the map.
 
The following code example shows how to select features for the State of California. This code will work on any feature layer with a STATE_NAME attribute—QueryFilters are not specific to any particular dataset.

[C#]
IQueryFilter queryFilter = new QueryFilterClass();
queryFilter.WhereClause = "STATE_NAME = 'California'";

ISelectionSet selectionSet = featureClass.Select(queryFilter,
  esriSelectionType.esriSelectionTypeIDSet,
  esriSelectionOption.esriSelectionOptionNormal, workspace);
There is no need to specify a WhereClause if you only want to filter the fields of data. You can also use the VB keyword "Nothing" or .NET keyword "null" in place of QueryFilter for those methods that require one.
 
For example, to count the features in a feature class, reference the following sample code:

[C#]
System.Windows.Forms.MessageBox.Show("There are " + featureClass.FeatureCount
  (null) + " states");
You can use the SubFields property to improve performance when using query filters. The performance gain comes from fetching only the field values that you require rather than all the data for each row. The default value for SubFields is "*", which indicates that all field values will be returned. It isn't necessary to set the subfields when the query filter is used in a context in which no attribute values are fetched, for example, when selecting features.
QueryFilter has properties on the IQueryFilter interface named SubFields and WhereClause and represents a subset of the single table queries that may be made against a table in a SQL database using the SQL SELECT statement. QueryFilters map on to simple SQL select statements of the form SELECT <field names> FROM <table name> WHERE <where-clause that references only table name>.
 
The SQL syntax used to specify the WhereClause of QueryFilter objects is the same as that of the underlying database holding the data. An application can use the ISQLSyntax interface on a Workspace to determine information about the SQL syntax used, such as the delimiter character used in qualifying table and field names and the identifier quote character. This information is available for the different types of workspaces (ArcSDE for Oracle, ArcSDE for SQLServer, Access, shapefile, coverage, and others). Unlike QueryDef objects, QueryFilter objects are supported across all the workspace types, including shapefiles and coverages.
 
A SpatialFilter object is a QueryFilter object that includes both spatial and attribute constraints. SpatialFilter can be used to restrict the set of features retrieved from a feature class using both spatial and attribute restrictions. A spatial filter has a single query geometry that specifies the geometry against which the features in the feature class will be tested. Because ArcObjects supports a number of different geometry types, including both single and multipart geometries and geometry collections, one way of expressing a complex spatial query is by building an appropriate query geometry to pass as input to the spatial filter.
 
The QueryFilter and SpatialFilter objects are shown in the following diagram.
 
 
A spatial filter has a single geometric shape that is used in the query. You can form more complicated spatial queries by using several spatial filters in succession.
 
You can use spatial filters anywhere that query filters are used, as long as the dataset to be queried has a spatial field. Some example tasks might be the following:
 
The ISpatialFilter interface is used to define a query with geographic criteria. You must always set these three properties: Geometry, GeometryField, and SpatialRel. The GeometryEx property can be used to set the query geometry in the case of large query geometries where the application is willing to surrender ownership of the geometry to the filter. In this case, the filter can modify (project) the query geometry in place if the spatial reference of the query geometry is different from the native spatial reference of the feature class or the requested output spatial reference. The spatial reference in which the features should be returned by the query is specified using the OutputSpatialReference property on the IQueryFilter interface.
 
The following code example shows a simple selection of features that intersect a given shape. It assumes an existing feature layer and a valid geometry pointer (perhaps derived from end user input).

[C#]
ISpatialFilter spatialFilter = new SpatialFilterClass();

spatialFilter.Geometry = geometry;
spatialFilter.GeometryField = featureClass.ShapeFieldName;
spatialFilter.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects;

ISelectionSet selectionSet = featureClass.Select(spatialFilter,
  esriSelectionType.esriSelectionTypeIDSet,
  esriSelectionOption.esriSelectionOptionNormal, workspace);
ISpatialFilter inherits the members of IQueryFilter—the previous code example could be extended by setting the WhereClause property on the spatial filter.
 
The SpatialRel property takes an enumeration that defines the relationship between the query geometry and the target feature geometry; this must be satisfied for the target feature to be returned by the query. The spatial relationships supported are the basic Clementini relationships, specified as part of the OpenGIS Simple Feature data-access standard.
 
The five basic Clementini relationships are Disjoint, Touches, Overlaps, Crosses, and Within. In esriSpatialRelEnum, esriSpatialRelIntersects, esriSpatialRelTouches, esriSpatialRelCrosses, esriSpatialRelOverlaps, esriSpatialRelWithin, and esriSpatialRelContains map to the corresponding Clementini relationships. Intersects maps to Not(Disjoint), Contains(a,b) maps to Within(b,a), and the rest correspond directly to the Clementini relationship.
 
esriSpatialRelEnvelopeIntersects is True if the envelope of the query geometry intersects the envelope of the target geometry.
 
esriSpatialRelIndexIntersects may be specified as the filter spatial relationship if the application is prepared to deal with features that do not intersect the query geometry, as long as all features that do intersect the query geometry are returned. This is a hint to the database that only the primary filter based on the spatial index needs to be applied; this results in faster query execution. This can be appropriate for drawing applications that rely on clipping to do the secondary filtering.
 
esriSpatialRelRelate can be specified as the filter spatial relationship if the application is to directly specify the relationships between the topological interior, boundary, and exterior of the query geometry and the topological interior, boundary, and exterior of the target geometry using the dimensionally extended nine-intersection model. The spatial relationships between the components are specified using a string of nine characters that is set as the value for the esriSpatialRelDescription property of the filter.
 
The characters are drawn from the alphabet {T, F, *} and indicate the dimension of the point set resulting from the intersection of the two components that map to that character position. F indicates no intersection, T indicates intersection, and * indicates it doesn't matter. The mapping of components to character position in the string is shown in the following diagram. The character string is constructed by reading out the entries in the 3 x 3 matrix in the order left to right and top to bottom. The values in this diagram translate into the nine-character string reading from left to right and top to bottom (TT*TT***).
 
 
Some of the spatial relationships exposed to the end user in the ArcMap Select By Location dialog box do not correspond directly to the basic Clementini relationships previously described.
 
These spatial relationships can be implemented using the Clementini spatial filter relationships combined with preprocessing and post processing. Preprocessing is used to assemble the appropriate query geometry (for example, in the case of distance-based relationships, using buffer). Post processing can be used to further restrict retrieved geometries returned by the Clementini operator. The following table shows examples of such processing: relationships between the query and target geometries.
 
 
The SpatialRelDescription property is only used when SpatialRel is set to esriSpatialRelRelation. You can use it to define various complex spatial relationships.
 
The SearchOrder property determines whether the spatial part of the query is performed before the attribute part of the query. By default, the spatial relationship is tested first, but in the case of queries where the attribute criteria are much more specific than the spatial, it is better to change the SearchOrder. An example of this kind of query is "find all worldwide cities with population greater than a million that are not in Spain".
 
If you want to want to query a feature class based on a collection of shapes, for example, "select the cities that are within the selected states", you have several options. One option is to apply successive spatial filters for each query shape. Another option is to make a single multipart query shape from the collection of original query shapes, then use a single spatial filter.
 
The following code example shows how to form a single geometry from the selected features in a layer.

[C#]
ISelectionSet selectionSet = featureClass.Select(spatialFilter,
  esriSelectionType.esriSelectionTypeIDSet,
  esriSelectionOption.esriSelectionOptionNormal, workspace);

EnumFeatureGeometry enumGeom = new EnumFeatureGeometryClass();

IEnumGeometryBind enumGeomBind = (IEnumGeometryBind)enumGeom;

enumGeomBind.BindGeometrySource(null, selectionSet);

ESRI.ArcGIS.Geometry.IGeometryFactory geomFactory =
  (ESRI.ArcGIS.Geometry.IGeometryFactory)new
  ESRI.ArcGIS.Geometry.GeometryEnvironmentClass();

ESRI.ArcGIS.Geometry.IGeometry geom = geomFactory.CreateGeometryFromEnumerator
  (enumGeom);
Selections
A SelectionSet object allows an application to reference a selected set of rows all belonging to a single table or feature class.
 
Selection sets are normally used throughout ArcObjects when a temporary subset of rows or features is required for some operation. It is important to note that the selection set only applies to a single table; you cannot sensibly combine two selection sets from different tables.
 
A selection set can be based on either a set of ObjectIDs that correspond to the selected rows, or on an actual set of Row objects, instantiated and referenced by the selection set. In either case, the selection set provides methods to iterate over the set of Row objects in the selection. The esriSelectionType property of a selection set, specified by an application at the time that it creates the selection set, determines the type of representation (object IDs or Row object references) used by the SelectionSet.
 
A selection set is typically created from a table or feature class using the Select method on the Table. A query filter is used to specify the subset of rows to include in the selection set as shown in the following code example.

[C#]
ISelectionSet selectionSet = featureClass.Select(queryFilter,
  esriSelectionType.esriSelectionTypeIDSet,
  esriSelectionOption.esriSelectionOptionNormal, workspace);
An application can create multiple SelectionSets on a Table or FeatureClass coclass. The SelectionSets reference their target table, but the latter have no knowledge of the selection sets that reference them. Applications are responsible for associating the created selection sets with the target table as appropriate. For example, a FeatureLayer in ArcMap holds a reference to a geodatabase FeatureClass and also to a SelectionSet that it creates on the feature class—at draw time the selected features are retrieved and drawn in a distinguished manner.
 
The ISelectionSet interface is used to manage and query the selection set. The Search method is used to iterate over the rows in the selection set and returns a cursor. The Search method takes a query filter that can be used to further restrict the set of rows in the selection that are returned for ID set selections. Using a query filter with hybrid selections will force the representation of the selection to become an ID set selection.
 
The Select method is used to create a new selection based on a subset of the current selection using a query filter to specify the restriction. The Add, AddList, and RemoveList methods can be used to alter the selection set by adding and removing rows specified by object IDs. The Combine method can be used to combine two SelectionSets using the standard set operations of union, intersection, difference, and symmetric difference. Only use Combine on two selection sets from the same target—it doesn't make sense to mix lists of IDs from different datasets.
 
The ISelectionSet2 interface provides an Update method that creates an update cursor on the selection set; this can be used to update and delete rows from the table or feature class of the selection set.
 
The following sample code returns the average population of the selected features in a counties feature layer; it illustrates a transition from using ArcMap objects to using the geodatabase data-access objects.

[C#]
ICursor cursor;

selectionSet.Search(null, true, out cursor);

IDataStatistics dataStats = new DataStatisticsClass();

dataStats.Cursor = cursor;
dataStats.Field = "POP1990";

ESRI.ArcGIS.esriSystem.IStatisticsResults statsResults = dataStats.Statistics;

double mean = statsResults.Mean;
As previously described in this section, the main interface for geodatabase selection sets is ISelectionSet. There are, however, some other similarly named interfaces in ArcObjects—the following table summarizes these other interfaces.
 
 
QueryDef
A QueryDef object represents a database query on one or more tables or feature classes.
 
QueryDef can be evaluated, resulting in the execution of the query on the database server. The results of the query are returned to the application as a cursor. The application can iterate over the cursor to fetch the row objects in the result set of the query. The row objects returned by a cursor on QueryDef are always of type esriGeoDatabase.Row—they never have custom behavior or support additional interfaces, even if the table names specified in QueryDef correspond to tables representing ObjectClasses with behavior. The row objects returned by evaluating QueryDef are read-only—these row objects do not reference a parent table, and the Store method may not be called on them. Attempting to store a row object returned by evaluating a QueryDef will result in an error.
 
The primary use of QueryDef is to directly evaluate database queries on arbitrary tables. They can be used to join tables with the assurance that the join query will execute in the underlying RDBMS. All of the tables in QueryDef must belong to the same workspace (RDBMS). QueryDef may include geometry fields in the specification of the list of fields to be returned but may not include geometry fields in the where clause specification unless the underlying DBMS is a spatially extended DBMS that supports geometric types, and unless the geometry fields for feature classes are using those native DBMS geometry types for storage.
 
The IQueryDef interface is used to set up and define the query and also provides an Evaluate method that is used to execute the query, returning a cursor.
 
The following code example shows how to create a QueryDef that defines a join between U.S. counties and states. A valid pointer to the workspace containing the data is assumed.

[C#]
IFeatureWorkspace featureWorkspace;

IQueryDef queryDef = featureWorkspace.CreateQueryDef();

queryDef.Tables = "Counties, States";
queryDef.SubFields = "COUNTIES.Shape, COUNTIES.NAME, STATES.STATE_ABBR";
queryDef.WhereClause = "COUNTIES.STATE_FIPS = STATES.STATE_FIPS";
QueryDef objects cannot be cocreated. They can only be created from the IFeatureWorkspace interface. This guarantees that all tables in the query are within the same workspace.
The SubFields property is optional when creating QueryDef objects. The default value is “*”, which means that all fields are returned.
 
The OpenFeatureQuery method on a workspace (available in the IFeatureWorkspace interface) can be used to create a FeatureClass that is based on a QueryDef. Such a feature class can be added to a Map (as a FeatureLayer) and can be used to visually represent the results of a database join query. Such a FeatureClass is similar in concept to an ArcSDE view.
 
The following code example gets a reference to a feature class based on the QueryDef created in the previous example. The IQueryDef.SubFields property must define one and only one spatial field to create the feature class.

[C#]
IFeatureClassContainer featureClassContainer =
  featureWorkspace.OpenFeatureQuery("My Counties Join", pQueryDef);

if (featureClassContainer.ClassCount != 1)
{
  System.Windows.Forms.MessageBox.Show(
    "failed to create feature class by query");
}

else
{
  featureClass = featureClassContainer.get_Class(0);
}
The SQL syntax used with QueryDef objects is the same as that of the underlying database holding the data. An application can use the ISQLSyntax interface on a Workspace to determine information about the SQL syntax for the database, such as the delimiter character used in qualifying table and field names and the identifier quote character.
QueryDefs represent a subset of the queries that can be made against an SQL database using the SQL SELECT statement. QueryDefs map onto simple SQL select statements of the form: SELECT <field names> FROM <list of table names> WHERE <where-clause referencing only tables in from list of tables>.
 
QueryDefs do not guarantee to support SQL statements that do not map onto the above simple form. In particular, QueryDefs do not guarantee to support ORDER BY and GROUP BY clauses embedded in the WhereClause property, nested SELECT statements or correlated subqueries in the WhereClause property, AS keywords embedded in the SubFields property, the use of table aliases in the Tables property, the use of Aggregate functions (for example, MIN, MAX, and SUM), or the use of DISTINCT clauses. Support for such capabilities is not guaranteed across all configurations, and applications that rely on such capabilities risk failure.
 
Relationships
A RelationshipClass object is an association between two object classes; one is the origin class and the other the destination class. The relationship class represents a set of relationships between the objects belonging to two classes. The relationship objects are detailed in the following diagram.
 
 
You can create a relationship class with either IRelationshipClassContainer or IFeatureWorkspace. RelationshipClass objects implement IDataset (useful for getting the name or the workspace), but they do not implement IClass (unless they are attributed). This is because a nonattributed relationship class does not have fields of its own.
 
The IRelationshipClass interface provides information about a relationship class, functionality to create and delete individual relationships, and methods to find related objects. The members of this interface can be split into three logical groups: the properties that correspond to how the relationship class was created, the object-to-object methods that deal with individual relationships, and the relationship rules methods.
 
The OriginPrimaryKey, OriginForeignKey, DestinationPrimaryKey, and DestinationForeignKey properties can be somewhat confusing—their uses are different depending on whether the relationship class is attributed as shown in the following graphic.
The object-to-object methods, such as GetObjectsRelatedToObjectSet, make use of the ISet interface, which manipulates a set of generic objects. When adding objects to a set with a cursor, make sure that the cursor recycling is turned off, as shown in the following code example (which deletes all the relationships for features with areas less than a certain value).

[C#]
IQueryFilter queryFilter = new QueryFilterClass();

queryFilter.WhereClause = "Shape_Area < 25";

IFeatureCursor featureCursor = featureClass.Search(queryFilter, false);

IFeature feature = featureCursor.NextFeature();

ESRI.ArcGIS.esriSystem.ISet featureSet = new ESRI.ArcGIS.esriSystem.SetClass();

while (feature != null)
{
  featureSet.Add(feature);
  feature = featureCursor.NextFeature();
}

featureSet.Reset();
relClass.DeleteRelationshipsForObjectSet(featureSet);
The Identify Results dialog box in ArcMap allows you to discover objects related to other objects through a relationship class and is shown in the following screen shot.
 
 
When using CreateRelationship, remember that this operation will write a value into the foreign key field. Therefore, you could overwrite, and therefore delete, an existing relationship. Similarly, DeleteRelationship will remove the foreign key value, so that field must allow null values unless you want to ensure that all objects in the class belong to relationships. The IRelationshipClass2 interface was added to provide a method to get matching objects.
 
The ArcMap Editor Property Inspector allows you to discover, add, and remove relationships for an object as shown in the following screen shot. 
 

An AttributedRelationshipClass object is a special kind of relationship class and is also a kind of table known as the relationship table. For nonattributed relationship classes, the relationships are stored with the objects themselves in the foreign key values. For attributed relationship classes, the relationships are defined by the objects in conjunction with the rows in the relationship table.
 
The AttributedRelationshipClass object is shown in the following diagram.
 
 
A good way of testing whether you have an AttributedRelationshipClass object is shown in the following sample code.

[C#]
if (relClass is ITable)
{
  System.Windows.Forms.MessageBox.Show(
    " relationship is an attributed relationship class ");
}
The IRelationshipClass.IsAttributed property only returns True if there are extra relationship attributes beyond those required to relate the objects. The IRelationshipClass.GetRelationship method is useful for accessing the relationship attributes.
 
A relationship represents a pair of related objects or features. Relationship is an abstract class that covers SimpleRelationship and AttributedRelationship objects. These objects are shown in the following diagram.
 
 
The IRelationship interface provides read-only information about a relationship. It is most useful with attributed relationships since it can form a bridge between the attribute information, which is in row form, and the related objects. When dealing with relationships, you will normally use the IRelationshipClass interface rather than IRelationship.
 
The SimpleRelationship object represents a pair of related geodatabase objects or features. There are no attribute values associated with SimpleRelationship. You should not cocreate SimpleRelationship. Instead, use IRelationshipClass.CreateRelationship. The AttributedRelationship object is a kind of row that represents a pair of related objects or features with additional information about the pairing. The additional information is stored in the row.
 
You should not cocreate AttributedRelationship. Instead, use IRelationshipClass.CreateRelationship. The IRelationshipClassEvents interface provides information as to when two objects are related or unrelated and when attributes on AttributedRelationship are modified. You can use this interface to listen to the OnChange, OnCreate, and OnDelete events on the Relationship class and execute custom behavior based on these events.
 
Class extension objects
ClassExtension is an object that allows you to customize and extend advanced geodatabase functionality. ClassExtensions also allow you to implement optional interfaces to customize geodatabase behavior. ClassExtension can also be used to add additional behavior to ObjectClass or FeatureClass by supporting custom interfaces. The class extension objects are detailed in the following diagram.
 
 
The IClassExtension interface is the main interface required for implementing ClassExtension.
 
The Init method provides a pointer to the ClassHelper object that should be used to access the extension object’s ObjectClass. ClassExtension should not maintain a reference to the ObjectClass directly, but rather should access it via ClassHelper as necessary. In addition to ClassHelper, PropertySet contains any data stored with ObjectClass. The value of PropertySet can be modified by using IClassSchemaEdit.AlterClassExtensionCLSID or IClassSchemaEdit2.AlterClassExtensionProperties. If the properties do not exist for the extension, the pExtensionProperties argument will be Nothing.
 
The Init method is called when ObjectClass is opened for the first time. Before ObjectClass is closed, the Shutdown method is called.
ClassHelper is passed as an argument to the Init method on the IClassExtension interface. ClassHelper is an intermediate object used to prevent circular references between ObjectClass and ClassExtension.
 
You can optionally implement the following class extension interfaces:
 
 
Domains and validation rules
The domain and validation rule objects are detailed in the following diagram.
 
 
Rules are associated with object classes and are used during the process of validating objects within an object class. There are four rule objects that are subclassed from the Rule abstract class:
 
Associating a rule with a class does not guarantee that all objects within the class will always be valid; the validation process still needs to be run through the Editor toolbar or with IValidation.Validate. Through the IValidation interface (on the Object class), the set of currently defined rules can be accessed, new rules can be added, and objects can be validated.
 
IRule is a generic interface that supports validation rules on an object class. Use this interface when you want to determine the type of rule and the helpstring associated with it. Helpstring displays the message associated with the rule. This message is displayed during the process of validating a single feature when that feature is found to be invalid. The helpstring of the first (of possibly many) validation rule that is found to be invalid is displayed through the ArcMap user interface (UI).
 
Type specifies the type of rule (attribute, relationship, or connectivity) and can be used to determine what validation rule object you are holding. Alternatively, you can probe for the appropriate interfaces (for example, if the rule supports IAttributeRule, then it is an AttributeRule).
 
The following Visual Basic for Applications (VBA) code extracts the rules defined for a layer called "pipes" and prints the type of the rule and helpstring associated with the rule.

[C#]
IValidation validation = (IValidation)objectClass;

IEnumRule enumRule = validation.Rules;

IRule rule = pEnumRule.Next();

while (rule != null)
{
  if (rule is IAttributeRule)
  {
    System.Windows.Forms.MessageBox.Show("Attribute rule - " + pRule.Type +
      " - " + pRule.Helpstring);
  }
  else if (rule is IRelationshipClass)
  {
    System.Windows.Forms.MessageBox.Show("Relationship rule - " + pRule.Type +
      " - " + pRule.Helpstring);
  }
  else if (rule is IJunctionConnectivityRule)
  {
    System.Windows.Forms.MessageBox.Show("JunctionConnectivity rule - " +
      pRule.Type + " - " + pRule.Helpstring);
  }
  else if (rule is IEdgeConnectivityRule)
  {
    System.Windows.Forms.MessageBox.Show("EdgeConnectivity rule - " +
      pRule.Type + " - " + pRule.Helpstring);
  }
  rule = pEnumRule.Next();
}
Creating a class extension can extend the types of rules that can be defined for an object class. By implementing IObjectClassValidation (along with IClassExtension), any type of custom validation rule can be coded.
 
Attribute rules and domains
An attribute rule applies an attribute domain to a field of an object class. The AttributeRule class is used to define attribute-specific rules on an object class. This type of rule applies a specified domain to a field name with a specific subtype. Domains can be used to limit the attribute values to a set of valid values or to a range of values. Domains can also define how values in the field are distributed during a split. The process of associating a domain with a field in an object class creates an AttributeRule as a side effect—thus, you generally don't need to explicitly create AttributeRules. The AttributeRule object is shown in the following diagram.
 
 
A domain is used to specify the permissible values that a field in an object class may take.
 
Domain is an abstract class that defines an interface used by RangeDomain and CodedValueDomain coclasses to constrain the permissible values that can be associated with a particular field on an object or feature class. Domains are assigned on a subtype basis. The RangeDomain and CodedValueDomain objects are shown in the following diagram.
 
 
The IDomain interface provides access to the common properties shared across both types of domains. Each property is read-write except the Type property. When creating and assigning a domain to a particular field, the client is required to set the Name and FieldType properties.