大容量插入具有地理空间数据类型的表时出现“未注册指定类型”错误

本文关键字:注册 错误 类型 插入 空间 数据类型 大容量 | 更新日期: 2023-09-27 17:57:15

我正在尝试使用System.Data程序集 (4.6.1) 中的 SqlBulkCopy 类来批量插入具有地理空间数据类型的表,使用大致如下所示的代码(改编自 https://github.com/MikaelEliasson/EntityFramework.Utilities):

public void InsertItems<T>(IEnumerable<T> items, string schema, string tableName, IList<ColumnMapping> properties, DbConnection storeConnection, int? batchSize)
{
    using (var reader = new EFDataReader<T>(items, properties))
    {
        var con = (SqlConnection)storeConnection;
        if (con.State != ConnectionState.Open)
        {
            con.Open();
        }
        using (var copy = new SqlBulkCopy(con))
        {
            copy.BatchSize = batchSize ?? 15000; //default batch size
            if (!string.IsNullOrWhiteSpace(schema))
            {
                copy.DestinationTableName = $"[{schema}].[{tableName}]";
            }
            else
            {
                copy.DestinationTableName = "[" + tableName + "]";
            }
            copy.NotifyAfter = 0;
            foreach (var i in Enumerable.Range(0, reader.FieldCount))
            {
                copy.ColumnMappings.Add(i, properties[i].NameInDatabase);
            }
            copy.WriteToServer(reader); // <-- throws here
            copy.Close();
        }
    }
}

这很好用,直到我尝试在包含地理空间数据的表上使用它。当我这样做时,我收到以下错误:

ERROR Swyfft.Console.TaskManager - Error running task SeedRating: 
(InvalidOperationException) The given value of type DbGeography from the data source cannot be converted to type udt of the specified target column.;   
(ArgumentException) Specified type is not registered on the target server.System.Data.Entity.Spatial.DbGeography, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089.;
   at Swyfft.Data.Utilities.SqlQueryProvider.InsertItems[T](IEnumerable`1 items, String schema, String tableName, IList`1 properties, DbConnection storeConnection, Nullable`1 batchSize) in C:'source'swyfft'swyf-website'Swyfft.Data.Utilities'SqlQueryProvider.cs:line 78
   at Swyfft.Data.Utilities.EFBatchOperation`2.InsertAll[TEntity](IEnumerable`1 items, DbConnection connection, Nullable`1 batchSize) in C:'source'swyfft'swyf-website'Swyfft.Data.Utilities'EFBatchOperation.cs:line 138
   at Swyfft.Data.Rating.RatingContext.BulkInsert[T](IEnumerable`1 entities, Nullable`1 batchSize) in C:'source'swyfft'swyf-website'Swyfft.Data.Rating'RatingContext.cs:line 69
   at Swyfft.Seeding.CsvLoaders.CsvLoader.ProcessCsv[T](StreamReader streamReader, String fileName, ISwyfftContext ctx, Func`2 parserFunc) in C:'source'swyfft'swyf-website'Swyfft.Seeding'CsvLoaders'CsvLoader.cs:line 133
   at Swyfft.Seeding.CsvLoaders.CsvLoader.InitializeCountyBlockQualities(String stateFilter) in C:'source'swyfft'swyf-website'Swyfft.Seeding'CsvLoaders'InitializeCountyBlockQualities.cs:line 35

我用谷歌搜索过,但无济于事。我已经沿着调用链向下追踪,深入到 SqlBulkCopy 程序集的内部(谢谢,Resharper!),但错误似乎隐藏得比我能够挖掘的更深。我尝试安装(并加载)适当的SQL Server Types软件包(https://www.nuget.org/packages/Microsoft.SqlServer.Types/),但没有骰子。

有什么建议吗?

大容量插入具有地理空间数据类型的表时出现“未注册指定类型”错误

好的,我想我把它修好了。有问题的代码在EFDataReader<T>类中(我从 https://github.com/MikaelEliasson/EntityFramework.Utilities/blob/master/EntityFramework.Utilities/EntityFramework.Utilities/EFDataReader.cs 借来的)。它的GetValue(int ordinal)最初看起来像这样:

public override object GetValue(int ordinal)
{
    return Accessors[ordinal](Enumerator.Current);
}

但这意味着它返回任何与数据库无关的DbGeometryDbGeography值,这些值恰好以DbGeometryDbGeography的形式出现,而 SqlBulkCopy 类无法理解这些值。它们实际上需要特定于SQL服务器,即SqlGeographySqlGeometry,如下所示:

public override object GetValue(int ordinal)
{
    object value = Accessors[ordinal](Enumerator.Current);
    var dbgeo = value as DbGeography;
    if (dbgeo != null)
    {
        var chars = new SqlChars(dbgeo.WellKnownValue.WellKnownText);
        return SqlGeography.STGeomFromText(chars, dbgeo.CoordinateSystemId);
    }
    var dbgeom = value as DbGeometry;
    if (dbgeom != null)
    {
        var chars = new SqlChars(dbgeom.WellKnownValue.WellKnownText);
        return SqlGeometry.STGeomFromText(chars, dbgeom.CoordinateSystemId);
    }
    return value;
}

Forward:我意识到我的专业知识还没有C#,所以我只能从我自己的ETL经验中汲取与你的错误相似的经验。 很可能,问题可能归结为您对 数据的格式良好的性质以及如何将其输入SQL

从 MSDN 访问空间数据类型告诉我们,数据需要格式正确。我们已经知道了...但是我们对源数据是这样假设的吗?

您正在使用来自外部来源的CSVLoader,根据我自己使用 SSIS 的经验,数据在文件中的结构并不总是正确。如前所述,SQL Server会拒绝违反列数据类型约束的格式错误的spatial data types

  • 在使用调用方法之前,是否已限定数据?
  • 您是否使用instantiable spatial datatypes
  • 您是否尝试拆分批量数据以测试CSVLoader检索它们的整个文件的一致性?也许只有部分数据已损坏。

由于这是一个集成操作,您是否考虑过设置临时表来处理假定格式正确的数据的清理/转换?

CSV文件是简单的文本文件,因此CSVLoaderSQL Server尝试将行批量插入数据库时之间存在隐式/显式转换。 SQL Server不能违反ACID要素。

我怎么强调都不为过,不要从您的数据中假设事实以及C#如何阅读和转换它们,更不用说SQL Server了。在意识到我的IS解析文件的方式无法处理CSV文件中的不一致(某些数据已损坏或丢失)之前,我花了很多小时在SSIS中苦苦挣扎CSV文件。

希望这将帮助您解决问题。

干杯