包含SqlGeometry的数据表导致存储过程执行失败...为什么?

Rav*_*sky 4 c# spatial sql-server-2008

我正在尝试将一系列SqlGeometry值保存到SQL Server 2008数据库.

基本上我在SQL Server存储过程中有一个tabletype,如下所示:

CREATE TYPE [dbo].[TableType_Example] AS TABLE
(
    [SpatialID] [bigint] NOT NULL,
    [RecordID] [bigint] NOT NULL,
    [geom] [geometry] NOT NULL
)
Run Code Online (Sandbox Code Playgroud)

然后我在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;
    }
Run Code Online (Sandbox Code Playgroud)

这适用于我的所有其他数据表,但是这一个包含一个SqlGeometry列,它会出现错误消息:

System.Data.dll中出现"System.ArgumentException"类型的异常,但未在用户代码中处理

附加信息:不支持"geom"列的类型.类型是'SqlGeometry'

这对我没有任何意义,因为我在文档中阅读的内容似乎支持该数据类型.

有什么想法吗?

编辑:

下面的评论和我链接的文章:https://viswaug.wordpress.com/2008/09/29/inserting-spatial-data-in-sql-server-2008/似乎表明我需要更改数据类型SqlGeometrySqlDbType.Udt.可悲的是,因为我正在使用数据表,所以无法定义,UdtTypeName = “GEOMETRY”;因为这是在参数上设置的.

Jon*_*amy 5

由于对您的问题做了简短的评论,我有机会充分利用这些选项.看来目前(甚至尝试.NET 4.6和SQL 2014)在为a定义列时不能将SqlGeographyOR 设置SqlGeometrytypeof()参数DataTable.为了绝对清晰,您可以在.NET中执行此操作甚至填充它,但您无法将该表作为TVP传递给存储过程.

有两种选择.

选项1.传递WKT格式的值.

定义您的表类型如下.

CREATE TYPE [dbo].[WKT_Example] AS TABLE
(
    [geom] [varchar](max) NOT NULL
)
Run Code Online (Sandbox Code Playgroud)

然后按如下方式定义存储过程.

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
Run Code Online (Sandbox Code Playgroud)

按如下方式定义.NET DataTable:

DataTable wktTable = new DataTable();
wktTable.Columns.Add("SpatialData", typeof(string));
Run Code Online (Sandbox Code Playgroud)

填充如下:

for (int j = 0; j < geometryCollection.Count; j++)
{
    System.Data.SqlTypes.SqlString wkt = geometryCollection[j].STAsText().ToSqlString();

    wktTable.Rows.Add(wkt.ToString());
}
Run Code Online (Sandbox Code Playgroud)

选项2.以WKB格式传递值.

定义您的表类型如下.

CREATE TYPE [dbo].[WKB_Example] AS TABLE
(
    [geom] [varbinary](max) NOT NULL
)
Run Code Online (Sandbox Code Playgroud)

然后按如下方式定义存储过程.

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
Run Code Online (Sandbox Code Playgroud)

按如下方式定义.NET DataTable:

DataTable wkbTable = new DataTable();
wkbTable.Columns.Add("SpatialData", typeof(System.Data.SqlTypes.SqlBytes));
Run Code Online (Sandbox Code Playgroud)

填充如下:

for (int j = 0; j < geometryCollection.Count; j++)
{
    wkbTable.Rows.Add(geographyCollection[j].STAsBinary());
}
Run Code Online (Sandbox Code Playgroud)

笔记:

按如下方式定义SqlParameter:

SqlParameter p = new SqlParameter("@rows", SqlDbType.Structured);
p.TypeName = "WKB_Example"; // The name of your table type
p.Value = wkbTable;
Run Code Online (Sandbox Code Playgroud)

我从地理工作中留下了4326的SRID.您可以将其更改为您想要的任何内容 - 事实上,如果您正在使用Geography我建议将其作为第二个参数来为您提供灵活性.

此外,如果性能至关重要,您会发现更好地使用WKB.我的测试发现WKB在WKT的45%到65%的时间内完成了WKB.这取决于数据的复杂程度和设置.

UdtTypeName当您的存储过程具有[几何]或[地理]类型的参数时,您在将参数指定为"几何"/"地理"时找到的信息是正确的.它不适用于TVP.