8

我正在尝试使用SqlBulkCopySQL Server 管理导入导出向导创建的平面文件中的类进行批量插入。文件以逗号分隔。

文件中的一行可能如下所示:

{DCAD82A9-32EC-4351-BEDC-2F8291B40AB3},,{ca91e768-072d-4e30-aaf1-bfe32c24008f},900001:1792756,900001:1792757,basladdning,2011-04-29 02:54,basladning8,2010-54:153. 04-29 02:54:15.380000000,{20A3C50E-8029-41DE-86F1-DDCDB9A78BA5}

我得到的错误是:

System.InvalidOperationException 未处理
Message=来自数据源的 String 类型的给定值无法转换为指定目标列的 uniqueidentifier 类型。

所以问题是从字符串转换为 GUID。

我尝试了以下方法:

  • 尝试了不同的编码,UTF8 和 ANSI
  • 移除了 GUIDS 周围的 { }
  • 在 GUIDS 周围添加了“”,有和没有 {}

有什么建议么?在 '' 所在的平面文件中,是一个也具有 Guid 作为数据类型的列。该列是 NULL,向导将其导出为 ''。这可能是问题吗?

编码:

using (var file = new StreamReader(filePath))
using (var csv = new CsvReader(file, false)) 
using (var bcp = new SqlBulkCopy(CreateConnectionString()))
{
    bcp.DestinationTableName = "table";
    bcp.WriteToServer(csv);
}

表定义:

CREATE TABLE [beata].[T_FeatureInstance]( 
    [FeatureInstanceId] [uniqueidentifier] NOT NULL, 
    [FeatureInstanceParentId] [uniqueidentifier] NULL, 
    [FeatureTypeAliasId] [uniqueidentifier] NOT NULL, 
    [Uuid] [varchar](1000) NOT NULL, 
    [VersionId] [varchar](50) NOT NULL, 
    [SkapadAv] [varchar](255) NULL, 
    [Skapad] [datetime] NOT NULL, 
    [UppdateradAv] [varchar](255) NOT NULL, 
    [Uppdaterad] [datetime] NOT NULL, 
    [Gs] [uniqueidentifier] NOT NULL,
4

4 回答 4

5

在您DataTable.Columns.Add包含数据类型作为第二个参数。这应该够了吧。例子:

DataTable dt = new DataTable();
dt.Columns.Add("ID", typeof(Guid));
于 2016-11-25T17:10:29.080 回答
4

问题是 in 中的空列{DCAD82A9-32EC-4351-BEDC-2F8291B40AB3},,{ca91e768-072d-4e30-aaf1-bfe32c24008f}被解释为''而不是NULL. 我刚刚在我的本地数据库上尝试了一个INSERT STATEMENT,当我指定NULL而不是'' 你可能会得到另一个错误时,那个错误就消失了:

Conversion failed when converting date and/or time from character string.

这样做的原因是 SQL 只允许毫秒的 3 位十进制精度。所以你必须四舍五入'2011-04-29 02:54:15.380000000''2011-04-29 02:54:15.380',它会正常工作。

一种方法是在这里提到 或者,您可以修改 CsvReader 代码以在 CsvReader.GetValue(int) 函数中的值为 string.Empty 时返回 DBNULL。看看这里

于 2012-11-29T16:03:10.283 回答
4

我想我为那些使用流行的免费软件代码类 BulkDataReader 的人找到了答案。我有同样的问题,我试图在不久前发布这个,但是因为我问了另一个问题并且没有提供答案,所以帖子被删除了。没关系,我没有意识到这是违反规则的。

这一次,我得到了答案,所以希望这篇文章能留在这里。我将保留我的问题的设置部分,以便它定义明确,并且没有人对为什么解决方案不适合他们感到困惑。我从一位同事那里获得了 BulkDataReader 类的代码,看来他可能是从另一个著名的答案来源获得的,所以每个人都会知道如何从搜索引擎中找到 BuldDataReader。

这个问题是这样设置的:

我也尝试了 CSV 文件中 GUIDS 的各种格式,包括:N'3192E434-F479-4B32-B516-AC658F4B7B89' {3192E434-F479-4B32-B516-AC658F4B7B89} (3192E434-F479-4B32-B516-AC658F4B7B7B89} ) “{3192E434-F479-4B32-B516-AC658F4B7B89}”</p>

我的 CSV 中的真实示例行是:1,AAPL,452.2012,2013-01-24 13:24:27.000,3192E434-F479-4B32-B516-AC658F4B7B89,3192E434-F479-4B32-B516-AC658F4B7B89

如果我删除 2 Guids 并导入没有这些列的表,它工作正常。使用 Guids to Unique Identifier 列我收到此错误:Message = {“来自数据源的 String 类型的给定值无法转换为指定目标列的类型唯一标识符。”}

我的控制代码非常基本,但 BulkDataReader 非常麻烦,因此如果您尝试调试它,请做好准备。

using (IDataReader reader = new BulkDataReader(new StreamReader("C:\\test.csv"), false))
        {
            String connectionStr = GetConnString();
            SqlConnection connection = new SqlConnection(connectionStr);
            connection.Open();

            SqlTransaction transaction = connection.BeginTransaction();
            try
            {
                SqlBulkCopy copy = new SqlBulkCopy(connection, SqlBulkCopyOptions.TableLock, transaction);
                copy.DestinationTableName = "TestTable6";
                copy.WriteToServer(reader); //ERROR OCCURS HERE: The given value of type String from the data source cannot be converted to type uniqueidentifier of the specified target column
                transaction.Commit();
            }
            catch (Exception exe)
            {
                transaction.Rollback();
            }
            finally
            {
                connection.Close();
            }
        }

所以错误实际上发生在 .NET SqlBulkCopy 类中。但是,它发生在 BulkDataReader 读取 Guid 值之后。该 GetValue 方法是从外部代码调用的,这意味着它隐藏在 BulkDataReader 的 streamReader 和 SqlBulkCopy 类中的 StreamWriting 内容之间的管道中。没有必要使用我最喜欢的反射实用程序深入研究所有内容。我发现当 BulkDataReader 的方法 IDataRecord.GetValue(int i) 返回一个真正是 Guid 的字符串时,SqlBulkCopy 无法将该字符串转换为唯一标识符,无论它采用什么格式。可能有一些晦涩的编码格式,但我可以找不到一个可以工作的。但是,如果我只是将值作为正确的 System.Guid 类型返回,那么 SqlBulkCopy 会将其转换为唯一标识符就好了。因此,一个似乎是噩梦般的序列化问题的简单解决方案。只需复制整个 IDataRecord.GetValue(int i) 方法,它应该可以工作。我尝试了许多从 CLR 到 SQL 的数据类型,但不是全部,所以可能还有另一个你必须玩这个反序列化游戏的地方,但这应该在很大程度上解决了这个问题。

  object IDataRecord.GetValue(int i)
    {
        ValidateDataReader(DataReaderValidations.IsInitialized | DataReaderValidations.IsNotClosed);

        if (((IDataRecord)this).IsDBNull(i))
            return DBNull.Value;
        else
        {
            //For some reason the SqlBulkCopy.WriteToServer method will call this GetValue method when reading 
            //the value and it doesn't seem to know how to convert the string value of a Guid to a unique identifier.
            //However, it does actually know how to convert a System.Guid to a UniqueIdentifer so we can return that if
            //the value parses to Guid
            if (IsGuid(this[i]))
                return Guid.Parse(this[i]);
            else
                return this[i];
        }
    }

    bool IsGuid(object value)
    {
        if (value != null)
        {
            Guid testGuid = Guid.Empty;
            if (Guid.TryParse(value.ToString(), out testGuid))
                return true;
        }

        return false;
    }

我希望这对某人有所帮助,很抱歉我第一次违反了博客规则。

于 2013-02-07T01:18:24.880 回答
0

向 uniqueidentifier 列中的标准插入看起来像这样(经过测试并且有效),因此可以回答格式化问题。该列是否允许空值?

插入 dbo.TableName([GUID])值('20A3C50E-8029-41DE-86F1-DDCDB9A78BA5')

于 2012-11-29T14:10:20.093 回答