使用SqlBulkCopy插入GUID

时间:2012-11-29 13:54:33

标签: c# sql-server sql-server-2008 sqlbulkcopy

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

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

  

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

我得到的错误是:

  

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

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

我尝试了以下内容:

  • 尝试了不同的编码,UTF8和ANSI
  • 删除了GUIDS周围的{}
  • 在GUID周围添加'',有无{}

有什么建议吗?在''是的平面文件中,是一个也有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 个答案:

答案 0 :(得分:4)

问题是{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',它才能正常工作。

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

答案 1 :(得分:3)

DataTable.Columns.Add中,将数据类型包含为第二个参数。这应该够了吧。 例如:

DataTable dt = new DataTable();
dt.Columns.Add("ID", typeof(Guid));

答案 2 :(得分:1)

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

这次,我得到了答案,希望这篇文章能留在这里。 我将离开我的问题的设置部分,因此它的定义很好,没有人对为什么解决方案不适合他们感到困惑。我从中获取了BulkDataReader类的代码 一个同事,似乎他可能从另一个着名的答案来源得到了这个,所以每个人都知道如何从搜索引擎中找到BuldDataReader。

此问题的设置如下:

我也在CSV文件中为GUIDS尝试了各种格式,包括: N'3192E434-F479-4B32-B516-AC658F4B7B89’ {3192E434-F479-4B32-B516-AC658F4B7B89} (3192E434-F479-4B32-B516-AC658F4B7B89) “{3192E434-F479-4B32-B516-AC658F4B7B89}”

我的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;
    }

我希望这可以帮助别人,抱歉我第一次破坏了博客规则。

答案 3 :(得分:0)

uniqueidentifier列中的标准插入看起来像这样(经过测试和工作),因此可以解决格式化问题。该列是否允许空值?

INSERT INTO dbo.TableName([GUID]) VALUES( '20A3C50E-8029-41DE-86F1-DDCDB9A78BA5')