EF ( Entity Framework) 操作ArcCataLog 生成的(Sql Server)空间数据库
因为项目需求,现在需要利用EF 操作由Arccatalog生成的sql server空间数据库.。在此之前,一直没有接触过空间数据库,在操作空间数据库时 绕了许多弯。。。 因此写一篇随笔做一个总结。
首先,我们了解一下空间数据库:空间数据库是指地理信息系统在计算机物理存储介质上存储的与应用相关的地理空间数据的总和,一般是以一系列特定结构的文件的形式组织在存储介质之上的。 在微软的官方文档中,有详细关于空间数据的介绍 https://docs.microsoft.com/zh-cn/sql/relational-databases/spatial/spatial-data-types-overview .文档中提出空间数据库有两种空间数据类类型: 一、geometry 数据类型支持平面或欧几里得(平面球)数据。 geometry数据类型符合开放地理空间联盟 (OGC) 的 SQL 简单特征规范 1.1.0 版 并符合 SQL MM(ISO 标准)。二、 geography 数据类型,该数据类型可存储诸如 GPS 纬度和经度坐标之类的椭圆体(圆球)数据。 此次项目涉及的空间数据类型为 geography 数据类型。 关于如何从ArcCataLog生成sql server 空间数据库 ,可以参考百度:http://jingyan.baidu.com/article/72ee561a55ef57e16138dfa0.html.
其次,了解了空间数据库的基本概念,我们开始尝试操作空间数据库。
在sql server 中查看 由ArccataLog 生成的数据库 :库中含有许多表, 我们暂时仅需要操作一张点表(已经含有位置和其他属性,lon 为经度,lat 为纬度)名字为ALLPOINT。点开表的设计,我们发现它与一般的表的区别: : 1 主键ID为:OBJECTID(未设置为自增长). 2 多了字段:value. 3. 多了字段:Shape (Shape 内容为{Point(经度,纬度)}不过这个精度比lon lat 精度高)
在实际应用中,我们需要对点的经纬度进行修改,或者添加新点,这时涉及数据的更新和添加,当我尝试在sql server 中编辑 数据时,sql server 总是报错,不能完成修改工作,因此决定利用sql 语句进行操作。 在实际应用中,我们需要对点的经纬度进行修改,此时必须使用Sql 语句进行修改 ,在网上查了一些资料:微软官方文档:https://msdn.microsoft.com/zh-cn/library/cc280487(v=sql.120).aspx, 通过这个文档,尝试操作数据库。其中:查询 和删除数据与普通数据库操作无异.,区别主要在更新。
sql 语句更新数据
1 更新: update [dbo].[ALLPOINT] SET Shape=geometry::STGeomFromText('POINT (114.93247099983682 24.868304000429305)',4326), where OBJECTID=4
通过这个语句,我们可以选取出OBJECTID为4的数据,并对数据中Shape的值进行更新, 其中 Point括号内部为经纬度值, 4326 为坐标系编号(这里特别提示,必须输入与ArcCatalog 选择坐标系一致的坐标系,在sql server 中这个字段 有一个约束:经纬度坐标必须为4326 .之前仿造 微软文档 填 0,一直报错。 )
2 添加:INSERT INTO [dbo].[LNALLPOINT](OBJECTID,value,lat,lon,Shape) VALUES (2,2.0000,114.93247099983682,24.868304000429305,geometry::STGeomFromText('POINT (114.93247099983682 24.868304000429305)',4326))
通过以上两个sql 语句 我们可以轻松的完成数据的更新与添加.
EF中更新数据
但 在项目中,我采用了EF框架 ,现在问题变成了:如何通过EF 操作空间数据库。我们观察 由EF生成Model中点表的结构:
我们发现点表的shape字段 变成了 public System.Data.Entity.Spatial.DbGeometry Shape { get; set; } 这么一个属性 . 在微软官方文档查询相当资料:https://msdn.microsoft.com/en-us/library/system.data.entity.spatial.dbgeometry/html
为了检验Shape具体情况 ,通过程序调试我们发现 Shape 的类型为:
图1 图2
如图1所示,shape有许多方法和属性均有红叉, 值为:函数求值类型需要运行所有线程. 图2 有一个属性ProviderValue 内容为 点的经度 ,纬度 ,仰角等...看到这里, 我就想尝试 通过获取ProviderValue的值,然后修改值. 我们通过观察ProviderValue ,发现 ProviderValue 类型为:
针对这个类型, 再次查找微软官方文档:https://msdn.microsoft.com/en-us/library/microsoft.sqlserver.types.sqlgeometry.aspx, 不过很可惜,ProviderValue的 STX,STY 属性为只读属性,所以想通过修改Shape的ProviderValue的想法无法实现. 这时,重新审视了 sql server 修改Shape 的语句, 想sql 语句可以拼写一个Shape对象,那么 EF肯定也可以,所以 查找 文档,终于在System.Data.Entity.Spatial.DbGeometry 中发现了FromText方法,
根据官方提示,可以通过这个方法 创建一个DbGeometry .经过 尝试,终于 修改成功. 部分代码如下:
var id = point.OBJECTID; //查询原始数据 ,将改变 的数据重新赋值 //特别 当点的经纬度改变 时 ,需要将Shape的经纬度进行改变 var monitorPointInfo = AllPointService.LoadEntities(u => u.OBJECTID == id && u.Deflag == 0).FirstOrDefault();string shapeString = ""; if (monitorPointInfo.lon != point.lon || monitorPointInfo.lat != point.lat) { //当 点的经纬度改变 ,Shape的值也需要改变 monitorPointInfo.lon = point.lon; monitorPointInfo.lat = point.lat; shapeString = "POINT("; shapeString += monitorPointInfo.lon + " " + monitorPointInfo.lat; shapeString += ")"; //通过这个方法重新创建 shape 只修改了点经纬度部分 ,其他部分未进行修改, 暂时不知道其他影响 monitorPointInfo.Shape = System.Data.Entity.Spatial.DbGeometry.FromText(shapeString, 4326); }
在EF中 实现修改时 ,有一个比较坑的地方: 就是修改时,不能够再查一次当前数据, 不然会报错:System.InvalidOperationException:“附加类型“******.Model.ALLPOINT”的实体失败,因为相同类型的其他实体已具有相同的主键值。在使用 "Attach" 方法或者将实体的状态设置为 "Unchanged" 或 "Modified" 时如果图形中的任何实体具有冲突键值,则可能会发生上述行为。这可能是因为某些实体是新的并且尚未接收数据库生成的键值。在此情况下,使用 "Add" 方法或者 "Added" 实体状态跟踪该图形,然后将非新实体的状态相应设置为 "Unchanged" 或 "Modified"。” 关于这个错误 ,可以参考:http://www.ithao123.cn/content-3651827.html
总结:
跳了好多坑,终于是暂时解决了在EF下修改空间数据库中 Shape 属性。还好文档给力,也许还有其他更好 的方法来修改数据,暂时只能这么做....