我试图将一系列SqlGeometry
值保存到SQL Server 2008数据库。
基本上我在SQL Server存储过程中有一个表格类型,如下所示:
CREATE TYPE [dbo].[TableType_Example] AS TABLE
(
[SpatialID] [bigint] NOT NULL,
[RecordID] [bigint] NOT NULL,
[geom] [geometry] NOT NULL
)
然后我在C#中构建一个数据表并像这样发送:
public static bool SaveSpatialDataElements(long recordID, List<BOSpatial> featureList)
{
//Setup features datatable
DataTable dtFeatures = new DataTable();
dtFeatures.Columns.Add("SpatialID", typeof(SqlInt64));
dtFeatures.Columns.Add("RecordID", typeof(SqlInt64));
dtFeatures.Columns.Add("geom", typeof(SqlGeometry));
foreach(var curFeature in featureList)
{
object[] curRowObjects = new object[dtFeatures.Columns.Count];
curRowObjects[0] = curFeature.SpatialID;
curRowObjects[1] = recordID;
using (var reader = new StringReader(curFeature.ToGML()))
{
using (var xmlreader = new XmlTextReader(reader))
{
curRowObjects[2] = SqlGeometry.GeomFromGml(new SqlXml(xmlreader), 0);
}
}
DataRow row = dtFeatures.NewRow();
row.ItemArray = curRowObjects;
dtFeatures.Rows.Add(row);
}
DbConn conn = new DbConn();
conn.Connect();
conn.ExecuteStoredProcedure(false, "USP_tblSpatialLocation_Update", recordID, dtFeatures);
conn.Disconnect();
return true;
}
这适用于我的所有其他数据表,但是这一个包含SqlGeometry
列,它会出现错误消息:
类型&#39; System.ArgumentException&#39;的例外情况发生在 System.Data.dll但未在用户代码中处理
附加信息:列的类型&#39; geom&#39;不受支持。 类型是&#39; SqlGeometry&#39;
这对我没有任何意义,因为我在文档中阅读的内容似乎支持该数据类型。
有什么想法吗?
编辑:
以下评论和我已关联的文章:https://viswaug.wordpress.com/2008/09/29/inserting-spatial-data-in-sql-server-2008/似乎表明我需要将SqlGeometry
的数据类型更改为SqlDbType.Udt
。可悲的是,当我使用数据表时,我无法定义UdtTypeName = “GEOMETRY”;
,因为这是在参数上设置的。
答案 0 :(得分:5)
由于对您的问题做了简短的评论,我有机会充分利用这些选项。目前看来(即使尝试.NET 4.6和SQL 2014),在为SqlGeography
定义列时,您无法将SqlGeometry
或typeof()
设置为DataTable
参数。为了绝对清晰,您可以在.NET中进行操作,甚至可以填充它,但是您无法将该表作为TVP传递给存储过程。
有两种选择。
选项1.传递WKT格式的值。
按如下方式定义表格类型。
CREATE TYPE [dbo].[WKT_Example] AS TABLE
(
[geom] [varchar](max) NOT NULL
)
然后按如下方式定义您的存储过程。
CREATE PROCEDURE [dbo].[BulkInsertFromWKT]
@rows [dbo].[WKT_Example] READONLY
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
INSERT INTO [dbo].[Table1]
([SpatialData])
SELECT
geometry::STGeomFromText(R.[SpatialData], 4326)
FROM
@rows R;
END
按如下方式定义.NET DataTable:
DataTable wktTable = new DataTable();
wktTable.Columns.Add("SpatialData", typeof(string));
按如下方式填充:
for (int j = 0; j < geometryCollection.Count; j++)
{
System.Data.SqlTypes.SqlString wkt = geometryCollection[j].STAsText().ToSqlString();
wktTable.Rows.Add(wkt.ToString());
}
选项2.传递WKB格式的值。
按如下方式定义表格类型。
CREATE TYPE [dbo].[WKB_Example] AS TABLE
(
[geom] [varbinary](max) NOT NULL
)
然后按如下方式定义您的存储过程。
CREATE PROCEDURE [dbo].[BulkInsertFromWKB]
@rows [dbo].[WKB_Example] READONLY
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
INSERT INTO [dbo].[Table1]
([SpatialData])
SELECT
geometry::STGeomFromWKB(R.[SpatialData], 4326)
FROM
@rows R;
END
按如下方式定义.NET DataTable:
DataTable wkbTable = new DataTable();
wkbTable.Columns.Add("SpatialData", typeof(System.Data.SqlTypes.SqlBytes));
按如下方式填充:
for (int j = 0; j < geometryCollection.Count; j++)
{
wkbTable.Rows.Add(geographyCollection[j].STAsBinary());
}
备注:强>
按如下方式定义您的SqlParameter:
SqlParameter p = new SqlParameter("@rows", SqlDbType.Structured);
p.TypeName = "WKB_Example"; // The name of your table type
p.Value = wkbTable;
我从地理位置工作中留下了4326的SRID。您可以将其更改为您想要的任何内容 - 事实上,如果您正在使用Geography
,我建议您将其作为第二个参数来为您提供灵活性。
此外,如果性能至关重要,您可以更好地使用WKB。我的测试发现WKB在WKT的45%到65%的时间内完成了WKB。这取决于数据的复杂程度和设置。
您在将参数&#39; s UdtTypeName
指定为&#34; Geometry&#34; /&#34;地理&#34;当您的存储过程具有[几何]或[地理]类型的参数时,这是正确的。它并不适用于TVP。
答案 1 :(得分:2)
将此作为替代答案,以防万一其他人遇到此问题并发现它有用,并且为了完整性尽可能多地包含这些信息。
如果您在c#代码中根本没有使用SqlGeometries(我只是将库包含在纯粹用于发送数据库值的话),那么坚持使用WKT似乎更有效。
最明确的做法是,Jon Bellamy在上面接受的答案中提出建议。然而,有一种似乎表现更好的更短的方式。基本上,数据库会将有效的WKT值隐式转换为存储过程中的几何。
示例:
表类型
CREATE TYPE [dbo].[WKT_Example] AS TABLE
(
[geom] Geometry NOT NULL
)
存储过程:
CREATE PROCEDURE [dbo].[BulkInsertFromWKB]
@rows [dbo].[WKB_Example] READONLY
AS
BEGIN
INSERT INTO [dbo].[Table1]
([SpatialData])
SELECT
geom
FROM
@rows
END
C#代码:
DataTable wkbTable = new DataTable();
wkbTable.Columns.Add("SpatialData", typeof(SqlString));
for (int j = 0; j < arrOfWKT.Count; j++)
{
wkbTable.Rows.Add(arrOfWKT[j]);
}
只是澄清一下。如果您的数据在c#代码中的格式为SqlGeometry,那么您可以更快地使用Jon Bellamy上面建议的WKB格式。
答案 2 :(得分:0)
我还尝试找出扩展数据表的方法,但没有管理。但是,我确实找到了一个使用自定义迭代的好方法,它可能比数据表更快,并且更灵活。
结帐这些链接:
A detailed explanation of how to use custom iterators to pass a table valued param (TVP) to SQL server sproc
An example using SQL geometry in a TVP
我值得注意的是,这种方法比WKT方法具有更轻的DB占用空间,因为它不需要DB将输入转换为可用格式。