目录
五万项目中,需要进行原始数据和编辑数据的对比检查,本文介绍开发相关功能的一些思考。
1 五万增量更新基本常识
1.1 操作及赋值情况
操作 | 描述 |
修改 | 修改要素:STACOD=修改、VERS=2020,FEAID不变 拆分要素:最大的STACOD=修改、VERS=2020,FEAID不变 |
增加 | 纯新增要素,STACOD=增加,VERS=2020,FEAID=null 由大要素派生要素:STACOD=增加,VERS=2020,FEAID=继承父要素 |
删除 | 标记删除:STACOD=删除、VERS=2020,其余属性及图形与原始一致 物理删除:要素在原始数据存在,而在编辑数据中不存在 |
原始 | 原始数据中要素,此类应为漏更新或无需更新要素 |
1.2 正确的情形
- 标记删除要素:
- STACOD=删除,VERS=2020
- 图形属性均不变
- 修改要素
- 大要素拆分
- 最大要素:STACOD=修改,VERS=2020,FEAID继承原始【派生修改】
- 非最大要素:STACOD=增加,VERS=2020,FEAID继承原始【派生增加】
- 单纯修改:STACOD=修改,VERS=2020,FEAID继承原始
- 大要素拆分
- 纯新增要素:
- STACOD=增加,VERS=2020
- FEAID=null
1.3 要检查的错误类型及错误提示
编号 | 错误类型 | 错误描述 |
1 | 物理删除拷回 | 以原始数据FEAID为基准,编辑数据中该FEAID未出现 |
2 | 删除修改 | STACOD=删除,但要素图形或属性发生变化 |
3 | 原始修改 | STACOD=原始,但要素图形或属性发生变化 |
4 | 派生增加 | 一个大要素分为多个要素,非最大要素STACOD=增加 |
5 | 派生修改 | 一个大要素分为多个要素,最大要素STACOD=修改 |
6 | 纯增加 | 编辑数据中要素FEAID=null |
7 | 原始未变化 | STACOD=修改或增加,但要素图形和属性均未变化 |
2 代码书写及测试
2.1 三种要素光标
2.1.1 IFeatureClass.Search()
搜索游标,频繁调用Store效率较低;不能使用UpdateFeature()和InsertFeature()方法进行要素更新和插入
//搜索游标
IFeatureCursor srcCursor= featureClass.Search(null, false);
IFeature pFeature = srcCursor.NextFeature();
while(pFeature != null)
{
pFeature.Set_value(pFeature.Fields.FindField("STACOD"), "修改");
pFeature.Store();
pFeature = srcCursor.NextFeature();
}
2.1.2 IFeatureClass.Update()
只能更新数据,必能插入
//更新游标
IFeatureCursor srcCursor= featureClass.Update(null, false);
IFeature pFeature = srcCursor.NextFeature();
while(pFeature != null)
{
pFeature.Set_value(pFeature.Fields.FindField("STACOD"), "修改");
srcCursor.UpdateFeature(pFeature);
pFeature = srcCursor.NextFeature();
}
srcCursor.Flush();
2.1.3 IFeatureClass.Insert()
只能插入,不能更新
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();
}
}
2.2 创建要素的两种主要方式:
- 使用IFeatureClass.CreateFeature 和 IFeature.Store
- 使用IFeatureClass.CreateFeatureBuffer 和 insert cursor
两种方式的区别:
- Store()方法
- 会触发所有对象行为,包括但不限于网络或注释的特定行为、要素参与的拓扑运算;也包括事件,如IObjectClassEvents 接口。
- Calling IFeature.Store results in all object behavior being triggered. This includes—but is not limited to—behavior specific to network or annotation features and features that participate in a topology, as well as events, such as those from the IObjectClassEvents interface.
- Insert cursors
- 插入光标用于向GDB中批量插入要素,使用插入光标和要素缓冲,提供了比简单的加载-创建要素-存储更快的性能
- 一个要注意的问题是使用 IFeatureCursor.InsertFeature 方法并不能确保复杂行为和事件被触发。
- Insert cursors are used to bulk insert features in the geodatabase. Using an insert cursor and a feature buffer offers significantly faster performance for simple data loading and creation than creating features and storing them. The one catch is that when using the IFeatureCursor.InsertFeature method, complex behavior and the triggering of events is not guaranteed.
IObjectClassInfo和IWorkspaceEditControl接口可用于通过insert cursors强制调用IFeature.Store,但这否定了它们提供的任何性能优势。【The IObjectClassInfo and IWorkspaceEditControl interfaces can be used to enforce the calling of IFeature.Store by insert cursors, but this negates any performance advantages they offer.】
2.2.1 创建和存储的方法
- CreateFeature方法被用来在地理数据库中创建单个要素,它的作用是分配一个系统ObjectID (OID),即OIDField字段的值
- Store 方法将这个新特征存储到数据库中
- 创建要素的基本过程为:要么是在点线面要素类,要么是在参与几何网络或拓扑构建的要素类中,创建一个独立的要素
【The CreateFeature method is used to create individual features in the geodatabase. It has the effect of assigning a system-generated ObjectID (OID), which is the value of the OIDField. Use the IFeature.Store method to store this new feature in the database.This basic process to create a feature is used independently of whether the feature is created in a point, line, polygon feature class, or in a feature class participating in a geometric network or topology.】
- Creating the feature:在一个要素类中调用CreateFeature 方法,其效果等同于在ITable 接口中调用CreateRow 方法,一旦方法被调用,其OID就是可用的;例外的情形是IFeatureClass 方法返回的是表示行对象的IFeature接口的引用【Calling the CreateFeature method on a feature class has the same effect as calling the CreateRow method on the ITable interface, except that the IFeatureClass method returns a reference to the IFeature interface representing the returned row object. The OID of the feature is available once the CreateFeature method is called.】
- Create the geometry for the feature:一个行对象与要素的基本差异之一就是要素和几何图形的关系,创建集合图形有很多不同的方式【One of the fundamental differences between a feature and a row object is the association between a feature and a geometry. There are many different ways to create the geometry for the feature. For more information on creating geometries, see How to create a polyline and How to create a polygon.】
- Store the geometry in the feature:使用IFeature.Shape 方法存储几何图形到要素中【Use the IFeature.Shape property to store the geometry with the feature.】
- Set the subtype, initialize any default values, and set other field values:调用CreateFeature方法后,默认的子类型和字段值不会被自动初始化【After calling CreateFeature, the default subtype value is not automatically set nor are default values initialized. For features without a subtype that have defined default values, calling IRowSubtypes.InitDefaultValues initializes the default values. For features created within a feature class containing subtypes, use IRowSubtypes.SubtypeCode to set the subtype value for features before calling IRowSubtypes.InitDefaultValues to ensure the default values are set for the particular subtype. After initializing the default values, the remaining field values can be set through the IFeature.Value property. If the field values are not correct for the type of field, an error is raised. However, if the field is too small to accept the defined value, an error is raised after the call to the Store method.】
- Store the feature:IFeature.Store 方法调用要素才会被存储到数据库中,一旦存储方法被调用,在统一编辑会话中的后续查询都将反映到要素中。【The feature is not written to the database until the IFeature.Store method has been called. Once Store has been called, all subsequent queries in the same edit session, using the geodatabase application programming interface (API), reflects the modified state of the feature.】
一旦IFeature.Store 方法调用,将会触发以下行为:
- 任何与特征相关的复杂行为都会发生。例如,网络要素创建相应的网络元素,拓扑在编辑的功能上创建dirty 区域,并且在覆盖IRowEvents接口的自定义功能上调用OnNew方法。【Any complex behavior inherent to the feature occurs. For example, network features create corresponding network elements, topologies create dirty areas over edited features, and the OnNew method is called on custom features that override the IRowEvents interface.】
- 如果要素属于实现IObjectClassEvents接口的拓展类,他的IObjectClassEvents.OnCreate也会被调用,这通常用于将对象的字段值设置为应用程序定义的变量【If the class the feature belongs to has a class extension that implements the IObjectClassEvents interface, its IObjectClassEvents.OnCreate method is called. This is often used to set an object's field values to application-defined variables.】
- 要素类的IObjectClassEvents接口公开事件。如果OnCreate事件有侦听器,则引发该事件,触发所有事件处理程序。【The IObjectClassEvents interface of a feature class exposes events. If the OnCreate event has listeners, the event is raised, triggering all event handlers.】
- 如果要素类参与具有通知的关系类,并且相关类具有实现IRelatedObjectClassEvents接口的类扩展,则会在扩展上触发RelatedObjectCreated方法。【If the feature's class participates in a relationship class with notification and the related class has a class extension that implements the IRelatedObjectClassEvents interface, the RelatedObjectCreated method is triggered on the extension.】
public static void CreateFeature(IFeatureClass featureClass, IPolyline polyline)
{
// Build the feature.
IFeature feature = featureClass.CreateFeature();
feature.Shape = polyline;
// Apply the appropriate subtype to the feature.
ISubtypes subtypes = (ISubtypes)featureClass;
IRowSubtypes rowSubtypes = (IRowSubtypes)feature;
if (subtypes.HasSubtype)
{
// In this example, the value of 3 represents the polymer vinyl chloride (PVC) subtype.
rowSubtypes.SubtypeCode = 3;
}
// Initialize any default values the feature has.
rowSubtypes.InitDefaultValues();
// Update the value on a string field that indicates who installed the feature.
int contractorFieldIndex = featureClass.FindField("CONTRACTOR");
feature.set_Value(contractorFieldIndex, "K Johnston");
// Commit the new feature to the geodatabase.
feature.Store();
}
2.2.2 使用插入光标创建
插入光标用于将行插入表中,并支持InsertRow方法。插入游标用于大容量行插入。与在表上多次调用CreateRow,然后在新行上调用Store方法相比,使用insert游标在将数据加载到简单表和要素类中时提供了更快的性能。
在通过参与拓扑、几何网络、自定义特性实现自定义行为的表上插入游标,一些类扩展将在内部使用CreateRow和Store方法来实现多态性(在这些情况下性能没有差异)。
InsertRow方法将行缓冲区作为参数。应用程序在插入行的表对象上使用CreateRowBuffer方法获取行缓冲区。每次调用光标上的InsertRow都会在数据库中创建一行,该行的初始值设置为输入行缓冲区中的值。一个单行缓冲区可用于多个InsertRow调用。所创建行的ObjectID由InsertRow方法返回。
请记住,如果要插入的几何图形位于与要插入的要素类不同的空间参照中,则必须投影这些几何图形,这可能会导致性能下降。为了避免这种情况,请确保要插入的几何图形与要插入的要素类使用相同的空间参照。
将真值传递给ITable.Insert方法的UseBuffering参数时,返回一个Insert游标,该游标缓冲客户端的行,并将它们成批发送到服务器以提高性能。始终启用缓冲,除非在编辑会话外部插入行(缓冲只能在编辑会话期间使用)。
应用程序负责在插入所有行之后调用insert游标上的Flush方法。如果未调用Flush方法,则游标在销毁时(当应用程序释放游标上的所有引用时)刷新其缓冲区。但是,依赖析构函数刷新插入游标并不能使应用程序有机会检测调用flush方法时可能出现的错误,例如,如果基础数据库中表的存储空间已满。下一个代码示例提供了一个基本模板来检查刷新是否成功。
如果使用空间缓存,则应在使用插入光标大容量加载要素之前禁用空间缓存。有关更多信息,请参阅ispatialchemanager。
下面的代码示例使用要素缓冲区将一组新要素插入到道路要素类中。特征的数量由geometryList参数的大小决定,特征从列表中按顺序获取其形状属性。所有功能的“TYPE”值都设置为“Primary Highway”。这些插入文件存储在缓冲区中,只有在调用Flush时才会刷新。
代码同2.1.3
2.2.3 使用仅加载模式插入行
IFeatureClassLoad接口是ArcSDE中的要素类以及文件Geodatabase中的要素类和表支持的可选接口。它可以用于启用“仅加载模式”,这可以提高数据加载的性能。仅在插入新行或要素时使用“仅加载”模式。编辑现有要素时不要使用“仅加载”模式。
使用ArcSDE,将要素类置于仅加载模式将禁用插入要素时更新空间索引。在文件geodatabase中,将要素类或表置于“仅加载”模式将禁用在插入行或要素时更新空间索引和属性索引。将要素类或表从仅加载模式中取出可重建索引。
当要素类或表处于仅加载模式时,其他应用程序无法处理数据。只有在通过ISchemaLock接口获取要素类上的独占架构锁之后,才能将要素类或表置于仅加载模式。
当启用或禁用仅加载模式时,应用程序负责确保类上没有未完成的游标。在启用仅加载模式之前实例化的任何游标都应在启用仅加载模式之前使用Marshal.releasecommobject或管理comleaser刷新(如果需要)并销毁,在禁用仅加载模式之前,应使用Marshal.ReleaseComObject或管理ComReleaser刷新和销毁以仅加载模式实例化的所有游标。否则可能导致意外错误。
下面的代码示例是前一个插入光标示例的修改版本,在插入发生之前启用了仅加载模式
//仅插入模式加载
public static void LoadOnlyModeInsert(IFeatureClass featureClass, List < IGeometry >
geometryList)
{
// 将FeatureClass转化为IFeatureClassLoad接口
IFeatureClassLoad featureClassLoad = (IFeatureClassLoad)featureClass;
//要素解锁,独占此数据库
ISchemaLock schemaLock = (ISchemaLock)featureClass;
try
{
schemaLock.ChangeSchemaLock(esriSchemaLock.esriExclusiveSchemaLock);
// 设置要素类为仅加载模式
featureClassLoad.LoadOnlyMode = true;
using(ComReleaser comReleaser = new ComReleaser())
{
//创建要素缓冲
IFeatureBuffer featureBuffer = featureClass.CreateFeatureBuffer();
comReleaser.ManageLifetime(featureBuffer);
// 创建插入光标
IFeatureCursor insertCursor = featureClass.Insert(true);
comReleaser.ManageLifetime(insertCursor);
// 获取TYPE字段索引,并将其赋值为Primary Highway
int typeFieldIndex = featureClass.FindField("TYPE");
featureBuffer.set_Value(typeFieldIndex, "Primary Highway");
//遍历几何要素列表
foreach (IGeometry geometry in geometryList)
{
//设置几何图形,并将要素缓冲插入
featureBuffer.Shape = geometry;
insertCursor.InsertFeature(featureBuffer);
}
//缓冲区刷新
insertCursor.Flush();
}
}
catch (Exception)
{
// 异常处理失败的情形
}
finally
{
// 解除要素类的仅加载模式
featureClassLoad.LoadOnlyMode = false;
//解除独占锁,设置为共享锁
schemaLock.ChangeSchemaLock(esriSchemaLock.esriSharedSchemaLock);
}
}
2.2.4 使用缓冲和刷新
当对插入光标使用缓冲时,对insert row的调用不会向数据库管理系统(DBMS)写入新行,而是向客户端缓冲区写入新行。当调用ICursor.Flush或当缓冲区达到其最大大小时,缓冲区中包含的行作为批处理操作写入DBMS。由于DBMS写入可以发生在Flush调用或InsertRow调用上(如果缓冲区已达到其最大大小),这两个调用都应该具有适当的错误处理。
缓冲只能在编辑会话期间使用。
还有其他一些事件可以触发刷新,比如游标的销毁(如前所述)。在编辑会话中,停止编辑操作将刷新已版本化的数据,而停止编辑会话将刷新未版本化的数据。此外,在编辑操作中创建搜索或更新光标将刷新已缓存的所有行。
下图显示了缓冲在企业地理数据库中的工作方式
X 重要参考
与本文相关的帮助文档中的示例:
|