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

标签 c# sql-server-2008 spatial

我正在尝试将一系列 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 列并且它因错误消息而失败:

An exception of type 'System.ArgumentException' occurred in System.Data.dll but was not handled in user code

Additional information: The type of column 'geom' is not supported. The type is 'SqlGeometry'

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

有什么想法吗?

编辑:

下面的评论和我链接的文章:https://viswaug.wordpress.com/2008/09/29/inserting-spatial-data-in-sql-server-2008/似乎建议我需要将 SqlGeometry 的数据类型更改为 SqlDbType.Udt。遗憾的是,当我使用数据表时,我无法定义 UdtTypeName = “GEOMETRY”;,因为这是在参数上设置的。

最佳答案

自从对您的问题做出简短评论后,我就有机会充分研究这些选项。看来目前(甚至尝试 .NET 4.6 和 SQL 2014)您无法在定义时将 SqlGeographySqlGeometry 设置为 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 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 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,我建议将其作为第二个参数以提供灵 active 。

此外,如果性能至关重要,您会发现使用 WKB 效果更好。我的测试发现 WKB 完成的时间是 WKT 所用时间的 45% 到 65%。这将因数据的复杂性和设置而异。

当您的存储过程具有 [Geometry] 或 [Geography] 类型的参数时,您找到的有关将参数的 UdtTypeName 指定为“Geometry”/“Geography”的信息是正确的。它不适用于 TVP。

关于c# - 包含 SqlGeometry 的数据表导致存储过程执行失败...为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29413490/

相关文章:

sql - 两列上的 group by 索引

sql - 如何开始创建和使用数据库?

r - R-在城市 map 上拟合网格并将数据输入到网格正方形中

c# - 通过坐标计算二维形状的最小外接矩形

c# - 删除具有外键约束的 Access 记录

c# - 如何从 C# 中的 SQL 查询结果填充类?

c# - mvc Controller 返回 'html data' 而不是 View

c# 十六进制字符串到字节图像和过滤

SQL 选择不同的列

MySQL - 从数据库中查找半径内的点