How to use cursors in the geodatabase


This document was published with and applies to ArcGIS 9.3.1.
A 9.2 version also exists.
Summary This topic discusses the use of cursors in the geodatabase and demonstrates when and how they can be used.

Development licensing Deployment licensing
ArcView ArcView
ArcEditor ArcEditor
ArcInfo ArcInfo
Engine Developer Kit Engine Runtime

In this topic


About using the cursors in the geodatabase

A cursor is a data access object that can be used to iterate over the set of rows in a table or insert new rows into a table. The following are the different types of cursors:
 
Each of the first three types of cursor is returned by the corresponding method (Search, Update, or Insert) from the ITable or IFeatureClass interfaces. A QueryDef cursor is returned from the IQueryDef.Evaluate method. The Search and Update methods of tables and feature classes take a query filter as input that can be used to restrict the set of rows returned and the IQueryDef.WhereClause property can be used before evaluation to restrict the set of rows returned by a QueryDef cursor.
 
The methods applicable to each type of cursor are all available in the single ICursor interface. It is the application developer's responsibility to make the method calls appropriate to the type of cursor, for example, InsertRow should not be called on a Search cursor.
 
A feature cursor will be returned if the dataset the cursor is created from contains a geometry field (such as a feature class). Cursors and feature cursors are used interchangeably throughout this topic, but aside from the minor differences in their method names (that is, NextRow and NextFeature), they're used in the same way. See the following illustration:
 
 
The type of row objects returned by a cursor depends on the type of table (that is, object class, feature class, or attributed relationship class) and its associated behavior.
 
Cursors are instantiated by methods, such as ITable.Search, but the execution of the cursor within the database management system (DBMS) only occurs when a row is retrieved or inserted, for example, by ICursor.NextRow. Because of this, calls to NextRow and InsertRow should typically be placed within try and catch blocks in the event that a DBMS error occurs.
 
Reaching the end of a cursor
To retrieve all rows in a cursor containing N rows, the application must make N calls to NextRow. Calling NextRow when the end of a cursor has been reached, returns a value of null in C# and Nothing in VB .NET. Cursors are not scrollable and do not support backing up and retrieving rows that have already been retrieved or making multiple passes over data. To step through the rows again, a second cursor must be created.
 
When using cursors in the .NET framework it is important to release a cursor when it's no longer needed. There are two ways to do this:
 
Both approaches are discussed in the article How to release COM references. The examples in this article will demonstrate how to manage cursors using the ComReleaser class.
 
Recycling
When instantiating a cursor using the Search or Update methods, the caller has the option to choose whether or not the cursor reuses (recycles) internal resources allocated for the current row. A cursor that is reusing resources is commonly referred to as a recycling cursor.
 
A cursor that is not reusing resources is commonly referred to as a non-recycling cursor. Cursors instantiated using the Insert method do not support recycling since they do not support the NextRow method.
 
Recycling cursors offer performance advantages, but they should only be used when the reference to the row object does not need to be persisted. Recycling cursors allocate a single row object and rehydrate it on each fetch. They can be used to optimize read-only access, such as during drawing. Row objects returned by a recycling cursor should not be modified. When using a recycling cursor, the row returned by NextRow is only valid until the next call to NextRow. Any subsequent call to NextRow invalidates the previous row. Callers should not use references to an earlier fetched row after a subsequent call to NextRow.
 
Non-recycling cursors return a unique instance for each row returned by NextRow. The rows returned by a non-recycling cursor can be modified (setting the IRow.Value property or any custom mutator supported by the row) and stored with polymorphic behavior. The geodatabase guarantees unique instance semantics on non-recycling 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, a reference to the existing row object will be returned. When using a non-recycling cursor, the row returned by NextRow is valid regardless of subsequent calls to NextRow. Callers can maintain references to the current row even if additional calls to NextRow are invoked.
 

Search cursors

A Search cursor can be used to retrieve rows specified by a QueryFilter and supports a NextRow method. A Search cursor can be used to fetch rows with the intent to update row values or delete rows. Search cursors that are used to update row values must be instantiated as non-recycling cursors, and those being used to delete rows should use recycling for better performance. Updating row values using a Search cursor is accomplished using a simple methodology of fetching the rows using a non-recycling cursor, setting the desired row values, and calling the Store method on the row.
 
When a Search cursor is used to retrieve a row within an edit session, it might be satisfied by a cache (the spatial cache or the object pool), eliminating the need to read the row from the DBMS. This can offer significant performance advantages. For more information, see Editing and deleting with cursors in this topic.
 
In the following code example, a Search cursor is used to find all the features in a feature class of towns and cities that intersect a spatial envelope and are classified as a city:
 

[C#]
public static void UseSearchCursor(IFeatureClass featureClass)
{
    // Create an envelope for the lower-right portion of data.
    IEnvelope envelope = new EnvelopeClass();
    envelope.PutCoords( - 66.4, 43.5,  - 61.9, 46.9);

    // Create a spatial query filter.
    ISpatialFilter spatialFilter = new SpatialFilterClass();

    // Specify the geometry to query with and the type of spatial operation to use.
    spatialFilter.Geometry = envelope;
    spatialFilter.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects;

    // Specify the name of the geometry field in the feature class.
    spatialFilter.GeometryField = featureClass.ShapeFieldName;

    // Specify the where clause and subfields.
    spatialFilter.WhereClause = "TERM = 'City'";
    spatialFilter.SubFields = "NAME";

    // Create a ComReleaser for cursor management.
    using(ComReleaser comReleaser = new ComReleaser())
    {
        // Perform the query and use a cursor to hold the results.
        IFeatureCursor searchCursor = featureClass.Search(spatialFilter, true);
        comReleaser.ManageLifetime(searchCursor);

        // Iterate through the features, viewing (as in this case) or editing them.
        IFeature feature = null;
        int nameFieldIndex = featureClass.FindField("NAME");
        while ((feature = searchCursor.NextFeature()) != null)
        {
            Console.WriteLine("City found: {0}", feature.get_Value(nameFieldIndex));
        }
    }
}

[VB.NET]
Public Shared Sub UseSearchCursor(ByVal featureClass As IFeatureClass)
' Create an envelope for the lower-right portion of data.
Dim envelope As IEnvelope = New EnvelopeClass()
envelope.PutCoords( -66.4, 43.5, -61.9, 46.9)

' Create a spatial query filter.
Dim spatialFilter As ISpatialFilter = New SpatialFilterClass()

' Specify the geometry to query with and the type of spatial operation to use.
spatialFilter.Geometry = envelope
spatialFilter.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects

' Specify the name of the geometry field in the feature class.
spatialFilter.GeometryField = featureClass.ShapeFieldName

' Specify the where clause and subfields.
spatialFilter.WhereClause = "TERM = 'City'"
spatialFilter.SubFields = "NAME"

' Create a ComReleaser for cursor management.
Using comReleaser As ComReleaser = New ComReleaser()

' Perform the query and use a cursor to hold the results.
Dim searchCursor As IFeatureCursor = featureClass.Search(spatialFilter, True)
comReleaser.ManageLifetime(searchCursor)

' Iterate through the features, viewing (as in this case) or editing them.
Dim feature As IFeature = searchCursor.NextFeature()
Dim nameFieldIndex As Integer = featureClass.FindField("NAME")
While Not feature Is Nothing
    Console.WriteLine("City found: {0}", feature.Value(nameFieldIndex))
    feature = searchCursor.NextFeature()
End While
End Using
End Sub

Update cursors

An Update cursor can be used to update and delete rows based on position and supports the NextRow, UpdateRow, and DeleteRow methods. Similar methods exist for feature cursors (NextFeature, UpdateFeature, and DeleteFeature).
 
In most cases, Update cursors are less efficient than Search cursors. One reason for this is that Update cursors are never satisfied by a spatial cache or an object pool; calling NextRow always results in a DBMS read. For more information on when an Update cursor should be used, see Editing and deleting with cursors in this topic.
 
Some methods should not be called on rows retrieved with an Update cursor, specifically IRow.Store and IRow.Delete. Instead, UpdateRow and DeleteRow should be used.
 
UpdateRow method
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 and call UpdateRow, passing in the modified row. This is an alternative to calling the Store method on a row retrieved from a Search cursor.
 
Using a non-recycling Update cursor can be faster than calling the Store method 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 require the Store method to trigger special behavior (such as features participating in geometric networks and topologies or objects from some classes with class extensions), calling UpdateRow on the cursor generates a call to the Store method on the row object to trigger the custom behavior (there will be no performance gain).
 
The row parameter of the UpdateRow method should always be the row currently pointed to by the cursor. Passing in a different row—a newly instantiated row, a row from a different position in the table, or a row from another table—should never occur.
 
In the following code example, an Update cursor is used to change the values of the "TYPE" field to "Toll Highway" in a feature class of roads, where the name of the road is "Highway 104":
 

[C#]
public static void UseUpdateCursor(IFeatureClass featureClass)
{
    // Restrict the number of features to be updated.
    IQueryFilter queryFilter = new QueryFilterClass();
    queryFilter.WhereClause = "NAME = 'Highway 104'";
    queryFilter.SubFields = "TYPE";

    // Create a ComReleaser for cursor management.
    using(ComReleaser comReleaser = new ComReleaser())
    {
        // Use IFeatureClass.Update to populate IFeatureCursor.
        IFeatureCursor updateCursor = featureClass.Update(queryFilter, false);
        comReleaser.ManageLifetime(updateCursor);

        int typeFieldIndex = featureClass.FindField("TYPE");
        IFeature feature = null;
        while ((feature = updateCursor.NextFeature()) != null)
        {
            feature.set_Value(typeFieldIndex, "Toll Highway");
            updateCursor.UpdateFeature(feature);
        }
    }
}

[VB.NET]
Public Shared Sub UseUpdateCursor(ByVal featureClass As IFeatureClass)
' Restrict the number of features to be updated.
Dim queryFilter As IQueryFilter = New QueryFilterClass()
queryFilter.WhereClause = "NAME = 'Highway 104'"
queryFilter.SubFields = "TYPE"

' Create a ComReleaser for cursor management.
Using comReleaser As ComReleaser = New ComReleaser()

' Use IFeatureClass.Update to populate IFeatureCursor.
Dim updateCursor As IFeatureCursor = featureClass.Update(queryFilter, False)
comReleaser.ManageLifetime(updateCursor)

Dim typeFieldIndex As Integer = featureClass.FindField("TYPE")
Dim feature As IFeature = updateCursor.NextFeature()
While Not feature Is Nothing
    feature.Value(typeFieldIndex) = "Toll Highway"
    updateCursor.UpdateFeature(feature)
    feature = updateCursor.NextFeature()
End While
End Using
End Sub
DeleteRow method
The DeleteRow method can be used to delete the row at the current position of an Update cursor and deletes the row returned by the last call to the NextRow method on this cursor.
 
After fetching the row object using NextRow, the application should call DeleteRow on the cursor to delete the row. References to a row should not be used after the row has been deleted. Using a recycling Update cursor to delete rows can be faster than calling delete on the rows returned by a Search cursor. Much like calling UpdateRow, this is often the case when performing direct updates outside an edit session on simple data.
 
If the row objects for the table are not simple, DeleteRow on the cursor generates a call to the Delete method on the row object to trigger custom behavior (there will be no performance gain).
 
The following code example uses an Update cursor to delete all features in a feature class of cities and towns that have TERM values of "Village":
 

[C#]
public static void DeleteRows(IFeatureClass featureClass)
{
    IQueryFilter queryFilter = new QueryFilterClass();
    queryFilter.WhereClause = "TERM = 'Village'";

    // Create a ComReleaser for cursor management.
    using(ComReleaser comReleaser = new ComReleaser())
    {
        // Create and manage a cursor.
        IFeatureCursor updateCursor = featureClass.Update(queryFilter, false);
        comReleaser.ManageLifetime(updateCursor);

        // Delete the retrieved features.
        IFeature feature = null;
        while ((feature = updateCursor.NextFeature()) != null)
        {
            updateCursor.DeleteFeature();
        }
    }
}

[VB.NET]
Public Shared Sub DeleteRows(ByVal featureClass As IFeatureClass)
Dim queryFilter As IQueryFilter = New QueryFilterClass()
queryFilter.WhereClause = "TERM = 'Village'"

' Create a ComReleaser for cursor management.
Using comReleaser As ComReleaser = New ComReleaser()

' Create and manage a cursor.
Dim updateCursor As IFeatureCursor = featureClass.Update(queryFilter, False)
comReleaser.ManageLifetime(updateCursor)

' Delete the retrieved features.
Dim feature As IFeature = updateCursor.NextFeature()
While Not feature Is Nothing
    updateCursor.DeleteFeature()
    feature = updateCursor.NextFeature()
End While
End Using
End Sub

Insert cursors

An insert cursor is used to insert rows into a table and supports the InsertRow method. Insert cursors are used for bulk row insertion. Using an insert cursor offers significantly faster performance for loading data into simple tables and feature classes than the alternative of making multiple calls to CreateRow on the table, then calling the Store method on the new row.
 
Insert cursors on tables that implement custom behavior through participation in topologies, geometric networks, custom features, and some class extensions will internally use the CreateRow and Store methods to achieve polymorphism (there is no difference in performance in these cases).
 
The InsertRow method takes a row buffer as an argument. Applications obtain a row buffer using the CreateRowBuffer method on the table object where rows are 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. A single row buffer can be reused for multiple InsertRow calls. The Object ID for the created row is returned by the InsertRow method.
 
It is important to keep in mind that if the geometries being inserted are in a different spatial reference than the feature class being inserted into that geometry will have to be projected which could result in a performance hit. To avoid this, it is recommended that the geometries being inserted are using the same spatial reference as the feature class that they will be inserted into.  
 
Passing a true value to the UseBuffering parameter of the ITable.Insert method returns an Insert cursor that buffers rows on the client side and sends them to the server in batches for increased performance. Always enable buffering, except when inserting rows outside an edit session (buffering can only be leveraged during an edit session).
 
The application is responsible for calling the Flush method on the Insert cursor after all rows have been inserted. If a call to the Flush method is not made, the cursor flushes 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 can arise on the call to the Flush method, for example, if the storage for the table in the underlying database fills up. The next code example provides a basic template to check if a flush is successful.
 
If a spatial cache is used, it should be disabled before bulk loading features using an Insert cursor. For more information, see ISpatialCacheManager.
 
The following code example uses a feature buffer to insert a set of new features into a feature class of roads. The number of features is determined by the size of the geometryList parameter, and the features take their Shape properties sequentially from the list. All  features have their "TYPE" value set to "Primary Highway." These inserts are stored in a buffer and are only flushed when Flush is called.
 

[C#]
public static void InsertFeaturesUsingCursor(IFeatureClass featureClass, List <
    IGeometry > geometryList)
{
    using(ComReleaser comReleaser = new ComReleaser())
    {
        // Create a feature buffer.
        IFeatureBuffer featureBuffer = featureClass.CreateFeatureBuffer();
        comReleaser.ManageLifetime(featureBuffer);

        // Create an insert cursor.
        IFeatureCursor insertCursor = featureClass.Insert(true);
        comReleaser.ManageLifetime(insertCursor);

        // All of the features to be created are classified as Primary Highways.
        int typeFieldIndex = featureClass.FindField("TYPE");
        featureBuffer.set_Value(typeFieldIndex, "Primary Highway");
        foreach (IGeometry geometry in geometryList)
        {
            // Set the feature buffer's shape and insert it.
            featureBuffer.Shape = geometry;
            insertCursor.InsertFeature(featureBuffer);
        }

        // Flush the buffer to the geodatabase.
        insertCursor.Flush();
    }
}

[VB.NET]
Public Shared Sub InsertFeaturesUsingCursor(ByVal featureClass As IFeatureClass, _
                                            ByVal geometryList As List(Of IGeometry))
Using comReleaser As ComReleaser = New ComReleaser()
' Create a feature buffer.
Dim featureBuffer As IFeatureBuffer = featureClass.CreateFeatureBuffer()
comReleaser.ManageLifetime(featureBuffer)

' Create an insert cursor.
Dim insertCursor As IFeatureCursor = featureClass.Insert(True)
comReleaser.ManageLifetime(insertCursor)

' All of the features to be created are classified as Primary Highways.
Dim typeFieldIndex As Integer = featureClass.FindField("TYPE")
featureBuffer.Value(typeFieldIndex) = "Primary Highway"
For Each geometry As IGeometry In geometryList
    ' Set the feature buffer's shape and insert it.
    featureBuffer.Shape = geometry
    insertCursor.InsertFeature(featureBuffer)
Next

' Flush the buffer to the geodatabase.
insertCursor.Flush()
End Using
End Sub

QueryDef cursors

A QueryDef represents a user-defined database query on one or more tables or feature classes. Calling IQueryDef.Evaluate returns a cursor that can be used to iterate through the query results (effectively a Search cursor). The rows returned from a QueryDef cursor are read-only. They do not reference a parent table; therefore, neither the IRow.Store method or the ICursor.UpdateRow method can be used to store any modifications. Attempting to perform either operation results in an error. Other methods that should not be used with Search cursors, such as DeleteRow and InsertRow, should not be used with a QueryDef cursor.
 
For more information about using QueryDefs and the IQueryDef interface, see How to join data.
 
The following code example shows how to use a QueryDef to join a feature class containing parcels and a table containing owner names, then steps through the results and shows the owner for each parcel:
 

[C#]
public static void DisplayParcelOwners(IWorkspace workspace)
{
    // Create a new QueryDef.
    IFeatureWorkspace featureWorkspace = (IFeatureWorkspace)workspace;
    IQueryDef queryDef = featureWorkspace.CreateQueryDef();

    // Setup the QueryDef to join the Parcels feature class and the owners table,
    // based on the PROPERTY_I and PROPERTY_ID fields. We can also restrict the
    // subfields here.
    queryDef.Tables = "Parcels, Owners";
    queryDef.WhereClause = "Parcels.PROPERTY_I = owners.PROPERTY_ID";
    queryDef.SubFields = "Parcels.PARCEL_ID, owners.OWNER_NAME";

    // Evaluate the QueryDef and display the results.
    using(ComReleaser comReleaser = new ComReleaser())
    {
        ICursor cursor = queryDef.Evaluate();
        comReleaser.ManageLifetime(cursor);

        int parcelIdIndex = cursor.FindField("Parcels.PARCEL_ID");
        int ownerNameIndex = cursor.FindField("owners.OWNER_NAME");
        IRow row = null;
        while ((row = cursor.NextRow()) != null)
        {
            Console.WriteLine("Parcel {0} owned by {1}", row.get_Value(parcelIdIndex)
                , row.get_Value(ownerNameIndex));
        }
    }
}

[VB.NET]
Public Shared Sub DisplayParcelOwners(ByVal workspace As IWorkspace)
' Create a new QueryDef.
Dim featureWorkspace As IFeatureWorkspace = CType(workspace, IFeatureWorkspace)
Dim queryDef As IQueryDef = featureWorkspace.CreateQueryDef()

' Setup the QueryDef to join the Parcels feature class and the owners table,
' based on the PROPERTY_I and PROPERTY_ID fields. We can also restrict the
' subfields here.
queryDef.Tables = "Parcels, Owners"
queryDef.WhereClause = "Parcels.PROPERTY_I = owners.PROPERTY_ID"
queryDef.SubFields = "Parcels.PARCEL_ID, owners.OWNER_NAME"

' Evaluate the QueryDef and display the results.
Using comReleaser As ComReleaser = New ComReleaser()
Dim cursor As ICursor = queryDef.Evaluate()
comReleaser.ManageLifetime(cursor)

Dim parcelIdIndex As Integer = cursor.FindField("Parcels.PARCEL_ID")
Dim ownerNameIndex As Integer = cursor.FindField("owners.OWNER_NAME")
Dim row As IRow = cursor.NextRow()
While Not row Is Nothing
    Console.WriteLine("Parcel {0} owned by {1}", row.Value(parcelIdIndex), _
                      row.Value(ownerNameIndex))
    row = cursor.NextRow()
End While
End Using
End Sub

Best practices

The following section describes the best practices for using cursors in certain situations and goes into more depth on topics which were briefly covered in the earlier sections.
 
Editing and deleting with cursors
Existing rows can be edited with a Search cursor or an Update cursor. When using a Search cursor, NextRow is called, returning a reference to a row. The row is edited and IRow.Store is called. When using an Update cursor, NextRow is called, the row is edited, and ICursor.UpdateRow is called with the row as a parameter. When using an Update cursor, "do not" call the Store method.
 
In the same way, rows can be deleted using a Search cursor or an Update cursor, with IRow.Delete used instead of IRow.Store, and ICursor.DeleteRow used instead of ICursor.UpdateRow.
 
When using a Search cursor to modify rows, "do not" use recycling. Using recycling can result in unexpected behavior.
The recommended approach depends on whether or not the edits are being made in an edit session, if the cursor is being used by ArcMap or an ArcGIS Engine application, and if the features being edited are simple or complex. The following table shows the approach to use in different situations:
 
 
ArcMap
ArcGIS Engine (simple)
ArcGIS Engine (complex)
Inside edit sessions
Search cursor
ArcSDE: Search cursor
Local GDB: Update cursor
ArcSDE: Search cursor
Local GDB: Update cursor
Outside edit sessions
Search cursor
Update cursor
ArcSDE: Search cursor
Local GDB: Update cursor
 
A Search cursor is always recommended in ArcMap, because the query might be satisfied by the contents of the map cache, making a DBMS query unnecessary.
 
When working with simple features and edit sessions in an ArcGIS Engine application, use a Search cursor to take advantage of batched updates in edit operations. Alternatively, with complex features, update calls are overridden by the features' custom behavior, which means the feature's store method will be called even if an update cursor is used.
 
When using cursors within edit sessions, cursors should be scoped to edit operations. See the following code example that changes the "TYPE" value in the features of a roads feature class to "Autoroute:"
 

[C#]
public static void EditWithSearchCursor(IWorkspace workspace, IFeatureClass
    featureClass, IQueryFilter queryFilter)
{
    // Find the index of the field to edit
    int typeFieldIndex = featureClass.FindField("TYPE");

    // Cast the workspace to an IWorkspaceEdit and start an edit session and edit operation.
    IWorkspaceEdit workspaceEdit = (IWorkspaceEdit)workspace;
    workspaceEdit.StartEditing(false);
    workspaceEdit.StartEditOperation();

    try
    {
        using(ComReleaser comReleaser = new ComReleaser())
        {
            // Create a non-recycling search cursor.
            IFeatureCursor searchCursor = featureClass.Search(queryFilter, false);
            comReleaser.ManageLifetime(searchCursor);

            // Edit the features matching the query.
            IFeature feature = null;
            while ((feature = searchCursor.NextFeature()) != null)
            {
                // Because the edit session is scoped to this method and feature
                // references should be scoped to edit sessions, the edited
                // features can be safely managed.
                comReleaser.ManageLifetime(feature);
                feature.set_Value(typeFieldIndex, "Autoroute");
                feature.Store();
            }
        }

        // Commit the edit operation and edit session.
        workspaceEdit.StopEditOperation();
        workspaceEdit.StopEditing(true);
    }
    catch (Exception)
    {
        // If an error occurs, it may be appropriate to discard the edits.
        workspaceEdit.AbortEditOperation();
        workspaceEdit.StopEditing(false);
    }
}

[VB.NET]
Public Shared Sub EditWithSearchCursor(ByVal workspace As IWorkspace, ByVal featureClass As IFeatureClass, ByVal queryFilter As IQueryFilter)
' Find the index of the field to edit
Dim typeFieldIndex As Integer = featureClass.FindField("TYPE")

' Cast the workspace to an IWorkspaceEdit and start an edit session and edit operation.
Dim workspaceEdit As IWorkspaceEdit = CType(workspace, IWorkspaceEdit)
workspaceEdit.StartEditing(False)
workspaceEdit.StartEditOperation()

Try
Using comReleaser As ComReleaser = New ComReleaser()
' Create a non-recycling search cursor.
Dim searchCursor As IFeatureCursor = featureClass.Search(queryFilter, False)
comReleaser.ManageLifetime(searchCursor)

' Edit the features matching the query.
Dim feature As IFeature = searchCursor.NextFeature()
While Not feature Is Nothing
    ' Because the edit session is scoped to this method and feature
    ' references should be scoped to edit sessions, the edited
    ' features can be safely managed.
    comReleaser.ManageLifetime(feature)
    feature.Value(typeFieldIndex) = "Autoroute"
    feature.Store()
    feature = searchCursor.NextFeature()
End While
End Using

' Commit the edit operation and edit session.
workspaceEdit.StopEditOperation()
workspaceEdit.StopEditing(True)
Catch exc As Exception
' If an error occurs, it may be appropriate to discard the edits.
workspaceEdit.AbortEditOperation()
workspaceEdit.StopEditing(False)
End Try
End Sub
Using load-only mode to insert rows
IFeatureClassLoad is an optional interface supported by feature classes in ArcSDE, and feature classes and tables in file geodatabases. It can be used to enable "load-only mode," which can improve the performance of data loading.
Load-only mode should only be used when inserting new rows or features. It should not be used when editing existing features.
With ArcSDE, putting a feature class in load-only mode disables updating of the spatial index while inserting features. In a file geodatabase, putting a feature class or table in load-only mode disables the updating of spatial and attribute indexes while inserting rows or features. Taking the feature class or table out of load-only mode rebuilds the indexes.
 
While a feature class or table is in load-only mode, other applications cannot work with the data. A feature class or table should be placed in load-only mode only after acquiring an exclusive schema lock on the feature class via the ISchemaLock interface.
Applications are responsible for ensuring there are no outstanding cursors on a class when load-only mode is enabled or disabled. Any cursors instantiated before enabling load-only mode should be flushed (if necessary) and destroyed using Marshal.ReleaseComObject before enabling load-only mode, and any cursors instantiated in load-only mode should be flushed and destroyed using Marshal.ReleaseComObject before disabling load-only mode. Failure to do so could result in unexpected errors.
The following code example is a modified version of the previous Insert cursor example, with load-only mode enabled before the inserts take place:
 

[C#]
public static void LoadOnlyModeInsert(IFeatureClass featureClass, List < IGeometry >
    geometryList)
{
    // Cast the feature class to the IFeatureClassLoad interface.
    IFeatureClassLoad featureClassLoad = (IFeatureClassLoad)featureClass;

    // Acquire an exclusive schema lock for the class.
    ISchemaLock schemaLock = (ISchemaLock)featureClass;
    try
    {
        schemaLock.ChangeSchemaLock(esriSchemaLock.esriExclusiveSchemaLock);

        // Enable load-only mode on the feature class.
        featureClassLoad.LoadOnlyMode = true;
        using(ComReleaser comReleaser = new ComReleaser())
        {
            // Create the feature buffer.
            IFeatureBuffer featureBuffer = featureClass.CreateFeatureBuffer();
            comReleaser.ManageLifetime(featureBuffer);

            // Create an insert cursor.
            IFeatureCursor insertCursor = featureClass.Insert(true);
            comReleaser.ManageLifetime(insertCursor);

            // All of the features to be created are classified as Primary Highways.
            int typeFieldIndex = featureClass.FindField("TYPE");
            featureBuffer.set_Value(typeFieldIndex, "Primary Highway");

            foreach (IGeometry geometry in geometryList)
            {
                // Set the feature buffer's shape and insert it.
                featureBuffer.Shape = geometry;
                insertCursor.InsertFeature(featureBuffer);
            }

            // Flush the buffer to the geodatabase.
            insertCursor.Flush();
        }
    }
    catch (Exception)
    {
        // Handle the failure in a way appropriate to the application.
    }
    finally
    {
        // Disable load-only mode on the feature class.
        featureClassLoad.LoadOnlyMode = false;

        // Demote the exclusive schema lock to a shared lock.
        schemaLock.ChangeSchemaLock(esriSchemaLock.esriSharedSchemaLock);
    }
}

[VB.NET]
Public Shared Sub LoadOnlyModeInsert(ByVal featureClass As IFeatureClass, ByVal geometryList As List(Of IGeometry))
' Cast the feature class to the IFeatureClassLoad interface.
Dim featureClassLoad As IFeatureClassLoad = CType(featureClass, IFeatureClassLoad)

' Acquire an exclusive schema lock for the class.
Dim schemaLock As ISchemaLock = CType(featureClass, ISchemaLock)
Try
schemaLock.ChangeSchemaLock(esriSchemaLock.esriExclusiveSchemaLock)

' Enable load-only mode on the feature class.
featureClassLoad.LoadOnlyMode = True
Using ComReleaser As ComReleaser = New ComReleaser()
' Create the feature buffer.
Dim featureBuffer As IFeatureBuffer = featureClass.CreateFeatureBuffer()
ComReleaser.ManageLifetime(featureBuffer)

' Create an insert cursor.
Dim insertCursor As IFeatureCursor = featureClass.Insert(True)
ComReleaser.ManageLifetime(insertCursor)

' All of the features to be created are classified as Primary Highways.
Dim typeFieldIndex As Integer = featureClass.FindField("TYPE")
featureBuffer.Value(typeFieldIndex) = "Primary Highway"
For Each geometry As IGeometry In geometryList
    ' Set the feature buffer's shape and insert it.
    featureBuffer.Shape = geometry
    insertCursor.InsertFeature(featureBuffer)
Next

' Flush the buffer to the geodatabase.
insertCursor.Flush()
End Using
Catch exc As Exception
' Handle the failure in a way appropriate to the application.
Finally
' Disable load-only mode on the feature class.
featureClassLoad.LoadOnlyMode = False

' Demote the exclusive schema lock to a shared lock.
schemaLock.ChangeSchemaLock(esriSchemaLock.esriSharedSchemaLock)
End Try
End Sub
Restricting the fields returned
When using a Search, Update, or QueryDef cursor to inspect or modify rows from a table, the rows being inspected or modified should be taken into account before creating the cursor. These cursors allow the use of a query filter to restrict the rows being returned, but the query filter can also be used to restrict the field values returned through the IQueryFilter.SubFields property. Requesting a subset of fields can offer significant performance advantages over retrieving an entire row.
 
The examples of Search, Update, and QueryDef cursors in the previous sections show how this is done. In some cases, additional fields might be retrieved if it's determined they are required; for example, a spatial filter will always return a feature's shape field, even if it isn't explicitly specified in the SubFields property.
 
For more information on using query filters, see How to query geodatabase objects.
 
Related methods
The ITable interface provides two methods that perform the same actions as an Update cursor, but in a more concise way: DeleteSearchedRows and UpdateSearchedRows. Calling these methods is equivalent to using an Update cursor to delete or modify the rows specified by the query filter parameter. In other words, the performance considerations for Update cursors (see Editing and deleting with cursors in this topic) also apply to these methods.
 
How buffering and flushing works
When buffering is used with an Insert cursor, a call to InsertRow does not write a new row to the DBMS, but to a client-side buffer. The rows contained in the buffer are written to the DBMS as a batched operation when ICursor.Flush is called, or when the buffer reaches its maximum size. Because a DBMS write can occur on either the Flush call or the InsertRow call (if the buffer has reached its maximum size), both of these calls should have appropriate error handling.
 
Buffering can only be leveraged during an edit session.
There are other events that can trigger a flush, such as the destruction of the cursor (as previously mentioned). Inside of an edit session, stopping an edit operation flushes versioned data, and stopping an edit session flushes non-versioned data. Also, creating a Search or Update cursor inside an edit operation flushes any rows that have already been cached.
 
The following illustration shows how buffering works in an enterprise geodatabase:
 
 
Updating and inserting outside of an edit session
Updating and inserting rows outside an edit session can offer increased performance when small numbers of rows or features are involved (what constitutes small is system dependent). Outside an edit session, transactions should be explicitly started and stopped using the ITransactions interface. If rows are updated or inserted outside an edit session or transaction and failure occurs, you cannot recover or rollback the operation.
 
Nested transactions are not supported. Attempting to start a transaction when a transaction has already been started does nothing. See the following illustration:
 
The ITransactionsOptions interface allows an auto-commit interval to be set. By default, a transaction is committed after 1000 rows are updated or inserted (with the transaction remaining open). To disable auto-committing, assign this property a value of 0.
 
The ITransactions interface is not required if the workspace is currently in an edit operation because the edit operation starts a database transaction.
The following code example shows how to execute updates or inserts outside of an edit operation, using the ITransactions interface:
[C#]
public void EditsOutsideAnEditSession(IWorkspace workspace)
{
    // Cast the workspace to the IWorkspaceEdit2 interface and see if the workspace
    // is in an edit operation. If so, raise an exception.
    IWorkspaceEdit2 workspaceEdit2 = (IWorkspaceEdit2)workspace;
    if (workspaceEdit2.IsInEditOperation)
    {
        throw new Exception("Cannot use ITransactions during an edit operation.");
    }

    // Begin a transaction.
    ITransactions transactions = (ITransactions)workspace;
    transactions.StartTransaction();
    try
    {
        // Perform updates and inserts here.

        // If the updates and inserts are successful, commit the changes.
        transactions.CommitTransaction();
    }
    catch (COMException comExc)
    {
        // If an error occurs during the inserts and updates, rollback the transaction.
        transactions.AbortTransaction();
    }
}

[VB.NET]
Public Sub EditsOutsideAnEditSession(ByVal workspace As IWorkspace)
    ' Cast the workspace to the IWorkspaceEdit2 interface and see if the workspace
    ' is in an edit operation. If so, raise an exception.
    Dim workspaceEdit2 As IWorkspaceEdit2 = CType(workspace, IWorkspaceEdit2)
    If workspaceEdit2.IsInEditOperation Then
        Throw New Exception("Cannot use ITransactions during an edit operation.")
    End If

    ' Begin a transaction.
    Dim transactions As ITransactions = CType(workspace, ITransactions)
    transactions.StartTransaction()
    Try
    ' Perform updates and inserts here.

    ' If the updates and inserts are successful, commit the changes.
    transactions.CommitTransaction()
    Catch comExc As COMException
    ' If an error occurs during the inserts and updates, rollback the transaction.
    transactions.AbortTransaction()
    End Try
End Sub


See Also:

How to query geodatabase objects
How to join data