ArcEngine开发中一些提高效率技巧
一 . 查询篇
1. AE的三种查询方式概述
在使用ArcEngine查询空间数据库的时候主要使用三种查询接口:
- IQueryFilter
- ISpatialFilter
- IQueryDef
三种接口使用场景总结如下表:
要求 | IQueryFilter | ISpatialFilter | IQueryDef |
---|---|---|---|
属性约束 | True | True | True |
空间约束 | False | True | False |
查询结果包括多个表的字段 | False | False | True |
查询结果返回一个游标 | True | True | True |
对象可以从结果中创建 | True | True | True |
返回的记录可以被编辑 | True | True | False |
Records include edits made in active edit session | True | True | False |
从上表可看出:
IQueryFilter 实现属性条件查询
ISpatialFilter 实现 属性+空间条件查询
IQueryDef 可实现属性查询,和多表查询,不能实现空间查询
2. IQueryFilter 接口参数设置技巧
SubFields
只设置需要查询的字段,这样比不设置和设置*查询速度会快很多。如果不设置这个属性,则会返回全部字段
注意:如果需要读取图形geometry,SubFields需要加 shape字段 ,否则报错;编辑数据时,一般不设置
WhereClause
优先使用大于、小于,其次才是不等于
索引的使用注意事项:
索引失效:值如 Area/30< 1000 换成Area<1000/30
索引失效:使用不等于运算符(<>、!=尽量不要计算字段,而去计算查询)
Recycling 参数 【 IFeatureClass.Search(filter, Recycling ) 】
True :循环使用对象,查询速度快,只存储一份对象 ,一般读操作时设置
False :不循环使用对象,查询速度慢,存储多份对象,一般写操作时设置
注意:如果设置为True,跳出循环以后,游标获取Feature不可以使用,
实例:
1
2
3
4
5IQueryFilter pQueryFilter = new QueryFilterClass();
string SubFields = "OID,Shape";
pQueryFilter.SubFields = SubFields;
ipFeatCursor = ipBufferFeaCls.Search(pQueryFilter, true);
ipFeature = ipFeatCursor.NextFeature();
3. ISpatialFilter 接口
Geometry
如果使用几何包对象作为过滤的几何图形,在为ISpatialFilter对象设置Geometry属性之前,先对几何包对象创建空间索引,这样能大大提高查询的效率,下面是创建空间索引的示例代码1
2
3
4// 将几何包对象转换到ISpatialIndex对象上去
ISpatialIndex spatialIndex = (ISpatialIndex)geometryBag;
spatialIndex.AllowIndexing = true;
spatialIndex.Invalidate();SubFields (同上)
WhereClause (同上)
Recycling 参数 (同上)
4. IQueryDef2 接口
IQueryDef2对象可由IFeatureWorkspace.CreateQueryDef 方法创建,一般这个速度最快,类似构建sql语句查表。该接口可同时可实现 distinct,group by,order by 等功能
注意事项:
1) 只能在ArcSDE、PGDB、FGDB数据源上使用(注意:shp数据不支持),数据集的历史表示 不被QueryDef游标支持(翻译可能不太准确,详见官方文档)。
2) 支持多表查询,但是查询的结果不能进行修改!
3) 使用IQueryDef查询出来的游标,用此游标获取的字段的别名和字段名一致(如果是要获取字段的中文别名,请使用其他方法获取游标)
参数说明
Tables
查询的表名称,支持多表查询,用逗号隔开即可,如”TableUser,TableRole”**
注意事项:如果使用的是SDE数据源,当当前的工作空间不是表所在的用户空间下,需要在表的前边加用户名前缀(如:Owner.MyTable)SubFields
查询的字段列表,默认不填为查询所有字段,即”*”,支持Distinct关键字,
格式:1) “*” 返回所有字段
2)”field1,field2,field3” 用逗号隔开要查询的字段
3)”table1.*,table2.field1,table3.field2” 查询table1的所有字段,table2的字段1,和table3的字段2
WhereClause
这块注意不同数据源,查询的SQL写法不同
PrefixClause
前缀查询条件,在Select 和 Select Column List之间,如Distinct关键字和ALL关键字
PrefixClause
后缀查询条件,在Select语句后,紧跟Where语句之后,如Order By
Evaluate
返回ICursor对象,这块使用的过程中记得用ComReleaser管理,在使用完毕释放Com对象
- 实例:
IQueryDef2 queryDef2 = (IQueryDef2)featureWorkspace.CreateQueryDef();
queryDef2.Tables = "Cities";
queryDef2.SubFields = "Name";
queryDef2.PostfixClause = "ORDER BY Name DESC";
(ComReleaser comReleaser = new ComReleaser())
{
ICursor cursor = queryDef2.Evaluate2(true);
comReleaser.ManageLifetime(cursor);
int nameIndex = cursor.FindField("Name");
IRow row = null;
while ((row = cursor.NextRow()) != null)
{
String cityName = Convert.ToString(row.get_Value(nameIndex));
}
}
二 . 统计篇
1. 统计两种方式
IQueryDef2接口
IDataStatistics接口
总结:
IQueryDef接口 无法进行空间查询统计,但是速度快
IDataStatistics 可进行空间查询统计,但是数据量大的时候比较慢
2. IDataStatistics接口
IDataStatistics的属性Statistics返回一个IStatisticsResult,可统计字段最大值 最小值 平均值 求和等:
获取唯一值:
IQueryFilter pQueryF = new QueryFilterClass();
pQueryF.SubFields = "CC";
IFeatureCursor pFeatureCursor = pCoverFC.Search(null, true);
IDataStatistics pDStatistics = new DataStatistics();
pDStatistics.Field = "CC";
pDStatistics.Cursor = (ICursor)pFeatureCursor;
IEnumerator pEnumerator = pDStatistics.UniqueValues;
统计其他:
ICursor pCursor = pFeaturelayer.Search(pQueryFilter, false) as ICursor;
IDataStatistics pData = new DataStatisticsClass();
pData.Cursor = pCursor;
pData.Field = pField.Name;
ESRI.ArcGIS.esriSystem.IStatisticsResults statisticsResults = pData.Statistics;
statisticsResults.Minimum;//最小值
statisticsResults.Maximum;//最大值
statisticsResults.Count;//数量
3. IQueryDef2接口
- 通过IQueryDef 中 group by 达到统计的效果,但是无法空间查询。同时可用distinct 来实现统计唯一值
1 | IDataset pDataset = pFeatureClass as IDataset; |
三 . 编辑篇
1. 新增插入
插入要素的两种方式
单个创建:IFeatureClass.CreateFeature() + IFeature.Store()
批量创建:IFeatureCursor.InsertFeature(IFeatureBuffer)
空间索引
大数据量进行数据插入的过程中,无需进行空间索引的创建,可以等批量插入完成之后,统一创建空间索引
IFeatureClassLoad.LoadOnlyMode =true 高效插入
注意事项
插入游标是用来实现要素批量插入功能的,单个要素的编辑尽量不使用插入游标
调用插入游标的Flush方法时要捕捉异常
不要在循环中重复FindField查找字段,给字段赋值,在循环前获取字段索引(适用于所有查询,插入,更新操作)
2. 更新
更新要素的两种方式
单个更新:IFeature.Store()
批量更新:IFeatureCursor.UpdateFeature(IFeature)
将一批数据更新为某一相同的属性IRowBuffer
注意事项
–使用Update Cursor更新注意其位置有效性,只对游标当前位置的
IFeature有效
3. 删除
删除要素的两种方式
IFeatureCursor 查询或IFeatureClass.GetFeature(OID) ,IFeature.Delete()(最慢,适合单个删除)
更新游标删除IFeatureCursor.DeleteFeature()(速度第三)
ITable.DeleteSearchedRows(IQueryFilter)删除 (速度第二)
IDataset.Workspace.ExecuteSQL(速度第一)
删除要素中所有要素删除(ITableWrite. Truncate)
注意事项
DeleteSearchedRows和ExecuteSQL属于批量删除,性能较优,但是整个过程不好 加进度条,界面不够友好
IFeatureCursor.DeleteFeature()虽次于前两者,可加界面进度条,用户体验会比较好
只针对非版本数据,且只有当前用户在用
四 . 内存释放篇
1. 需要释放的对象
- ICursor对象务必及时释放。在每次获取查询统计时获取游标后必须释放
- IEnumXX接口。
- GetXX方法获取的对象,比如IFeatureClass的GetFeature方法
- IPointCollection的GetPoint方法
- IWorkspace
- 打开单个文件数据库后的方法
- Mxd文档打开后的关闭
2. 尽可能使用Esri提供的MangeLifetime来管理ArcGIS Engine对象的生命周期
using(ComReleaser comReleaser = new ComReleaser())
{
//读取查询结果
IFeatureCursor featureCursor = featureClass.Search(spatialFilter, true);
comReleaser.ManageLifetime(featureCursor);
IFeature feature = null;
while ((feature = featureCursor.NextFeature()) != null)
{
String roadName = Convert.ToString(feature.get_Value(nameIndex));
String roadType = Convert.ToString(feature.get_Value(typeIndex));
Console.WriteLine("Name: {0}, Type: {1}", roadName, roadType);
//这块强烈建议feature释放掉,如果做大数据操作的时候这块不释放 内存会撑爆的。
System.Runtime.InteropServices.Marshal.ReleaseComObject(feature);
}
}
五 . 多线程篇
1.多线程使用注意事项
ArcGIS Engine支持多线程,但是不支持多个线程之间的彼此调用
• AO对象不能在线程中间共享,比如必须在各自的线程中重新打开Workspace、 FeatureClass。
•主线程向工作线程传递AO对象,可以将对象序列化成字符串,再将字符串传递 给目标线程,然后再反序列化还原到对象(待验证)
•多线程处理中涉及GP工具,保证操作数据未被使用或占用,一般不能成功