SqlBulkCopy - 数据源中String类型的给定值无法转换为指定目标列的类型money

时间:2013-08-09 04:25:09

标签: c# datatable sqlbulkcopy

尝试从DataTable执行SqlBulkCopy时遇到此异常。

Error Message: The given value of type String from the data source cannot be converted to type money of the specified target column.
Target Site: System.Object ConvertValue(System.Object, System.Data.SqlClient._SqlMetaData, Boolean, Boolean ByRef, Boolean ByRef)

我理解错误说的是什么,但我怎样才能获得更多信息,例如行/字段正在发生?数据表由第三方填充,最多可包含200列和最多10k行。返回的列取决于发送给第三方的请求。所有 数据表 列都是字符串类型。我的数据库中的列不是所有varchar,因此,在执行插入之前,我使用以下代码格式化数据表值(删除非重要代码):

//--- create lists to hold the special data type columns
List<DataColumn> IntColumns = new List<DataColumn>();
List<DataColumn> DecimalColumns = new List<DataColumn>();
List<DataColumn> BoolColumns = new List<DataColumn>();
List<DataColumn> DateColumns = new List<DataColumn>();

foreach (DataColumn Column in dtData.Columns)
{
    //--- find the field map that tells the system where to put this piece of data from the 3rd party
    FieldMap ColumnMap = AllFieldMaps.Find(a => a.SourceFieldID.ToLower() == Column.ColumnName.ToLower());

    //--- get the datatype for this field in our system
    Type FieldDataType = Nullable.GetUnderlyingType(DestinationType.Property(ColumnMap.DestinationFieldName).PropertyType);

    //--- find the field data type and add to respective list
    switch (Type.GetTypeCode(FieldDataType))
    {
        case TypeCode.Int16:
        case TypeCode.Int32:
        case TypeCode.Int64: { IntColumns.Add(Column); break; }
        case TypeCode.Boolean: { BoolColumns.Add(Column); break; }
        case TypeCode.Double:
        case TypeCode.Decimal: { DecimalColumns.Add(Column); break; }
        case TypeCode.DateTime: { DateColumns.Add(Column); break; }
    }

    //--- add the mapping for the column on the BulkCopy object
    BulkCopy.ColumnMappings.Add(new SqlBulkCopyColumnMapping(Column.ColumnName, ColumnMap.DestinationFieldName));
}

//--- loop through all rows and convert the values to data types that match our database's data type for that field
foreach (DataRow dr in dtData.Rows)
{
    //--- convert int values
    foreach (DataColumn IntCol in IntColumns)
        dr[IntCol] = Helpers.CleanNum(dr[IntCol].ToString());

    //--- convert decimal values
    foreach (DataColumn DecCol in DecimalColumns)
        dr[DecCol] = Helpers.CleanDecimal(dr[DecCol].ToString());

    //--- convert bool values
    foreach (DataColumn BoolCol in BoolColumns)
        dr[BoolCol] = Helpers.ConvertStringToBool(dr[BoolCol].ToString());

    //--- convert date values
    foreach (DataColumn DateCol in DateColumns)
        dr[DateCol] = dr[DateCol].ToString().Replace("T", " ");
}

try
{
    //--- do bulk insert
    BulkCopy.WriteToServer(dtData);
    transaction.Commit();
}
catch (Exception ex)
{
    transaction.Rollback();

    //--- handles error
    //--- this is where I need to find the row & column having an issue
}

此代码应格式化其目标字段的所有值。在这个错误的情况下,十进制,清除它的函数将删除任何不是0-9或的字符。 (小数点)。抛出错误的字段在数据库中可以为空。

2级异常有这个错误:

Error Message: Failed to convert parameter value from a String to a Decimal.
Target Site: System.Object CoerceValue(System.Object, System.Data.SqlClient.MetaType, Boolean ByRef, Boolean ByRef, Boolean)

并且3级异常有这个错误:

Error Message: Input string was not in a correct format
Target Site: Void StringToNumber(System.String, System.Globalization.NumberStyles, NumberBuffer ByRef, System.Globalization.NumberFormatInfo, Boolean)

有没有人有任何想法要修复?或任何想要获得更多信息的想法?

9 个答案:

答案 0 :(得分:51)

对于那些在这个问题上遇到困难并且收到关于nvarchar而不是钱的类似错误消息的人:

  

数据源中String类型的给定值无法转换为指定目标列的nvarchar类型。

这可能是由于列太短造成的。

例如,如果您的列定义为nvarchar(20)并且您有40个字符的字符串,则可能会出现此错误。

Source

答案 1 :(得分:33)

请使用SqlBulkCopyColumnMapping。

示例:

private void SaveFileToDatabase(string filePath)
{
    string strConnection = System.Configuration.ConfigurationManager.ConnectionStrings["MHMRA_TexMedEvsConnectionString"].ConnectionString.ToString();

    String excelConnString = String.Format("Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties=\"Excel 12.0\"", filePath);
    //Create Connection to Excel work book 
    using (OleDbConnection excelConnection = new OleDbConnection(excelConnString))
    {
        //Create OleDbCommand to fetch data from Excel 
        using (OleDbCommand cmd = new OleDbCommand("Select * from [Crosswalk$]", excelConnection))
        {
            excelConnection.Open();
            using (OleDbDataReader dReader = cmd.ExecuteReader())
            {
                using (SqlBulkCopy sqlBulk = new SqlBulkCopy(strConnection))
                {
                    //Give your Destination table name 
                    sqlBulk.DestinationTableName = "PaySrcCrosswalk";

                    SqlBulkCopyColumnMapping AdmissionPaySrcID=new SqlBulkCopyColumnMapping("AdmissionPaySrcID","AdmissionPaySrcID");
                    sqlBulk.ColumnMappings.Add(AdmissionPaySrcID);

                    SqlBulkCopyColumnMapping TMHP_Detail = new SqlBulkCopyColumnMapping("TMHP_Detail", "TMHP_Detail");
                    sqlBulk.ColumnMappings.Add(TMHP_Detail);

                    SqlBulkCopyColumnMapping PaySrcType = new SqlBulkCopyColumnMapping("PaySrcType", "PaySrcType");
                    sqlBulk.ColumnMappings.Add(PaySrcType);

                    SqlBulkCopyColumnMapping AgencyID = new SqlBulkCopyColumnMapping("AgencyID", "AgencyID");
                    sqlBulk.ColumnMappings.Add(AgencyID);

                    SqlBulkCopyColumnMapping CountyCode = new SqlBulkCopyColumnMapping("CountyCode", "CountyCode");
                    sqlBulk.ColumnMappings.Add(CountyCode);

                    SqlBulkCopyColumnMapping EntityID = new SqlBulkCopyColumnMapping("EntityID", "EntityID");
                    sqlBulk.ColumnMappings.Add(EntityID);

                    sqlBulk.WriteToServer(dReader);
                }
            }
        }
    }
}  

答案 2 :(得分:20)

@Corey - 它只是删除所有无效字符。但是,你的评论让我想到了答案。

问题是我的数据库中的许多字段都可以为空。使用SqlBulkCopy时,不会将空字符串作为空值插入。所以在我的字段不是varchar(bit,int,decimal,datetime等)的情况下,它试图插入一个空字符串,这显然对该数据类型无效。

解决方案是修改我的循环,我在其中验证值(对于非字符串的每个数据类型重复)

//--- convert decimal values
foreach (DataColumn DecCol in DecimalColumns)
{
     if(string.IsNullOrEmpty(dr[DecCol].ToString()))
          dr[DecCol] = null; //--- this had to be set to null, not empty
     else
          dr[DecCol] = Helpers.CleanDecimal(dr[DecCol].ToString());
}

完成上述调整后,所有内容都会毫无问题地插入。

答案 3 :(得分:9)

由于我不相信"Please use..." plus some random code that is unrelated to the question是一个很好的答案,但是我确实相信这种精神是正确的,所以我决定正确回答这个问题。

使用Sql Bulk Copy时,它将尝试将输入数据直接与服务器上的数据对齐。因此,它将使用服务器表并执行类似于以下内容的SQL语句:

INSERT INTO [schema].[table] (col1, col2, col3) VALUES

因此,如果为它提供第1列,第3列和第2列,即使您的名字也可以匹配(例如:col1,col3,col2)。它将像这样插入:

INSERT INTO [schema].[table] (col1, col2, col3) VALUES
                          ('col1', 'col3', 'col2')

Sql大容量插入必须确定列映射是额外的工作和开销。因此,它允许您选择...要么确保您的代码和SQL表列的顺序相同,要么明确声明要按列名对齐。

因此,如果您的问题是列未对齐(这可能是导致此错误的主要原因),那么此答案适合您。

TLDR

using System.Data;
//...
myDataTable.Columns.Cast<DataColumn>().ToList().ForEach(x => 
    bulkCopy.ColumnMappings.Add(new SqlBulkCopyColumnMapping(x.ColumnName, x.ColumnName)));

这将使用您现有的DataTable,尝试将其插入到创建的BulkCopy对象中,并且它将显式地将name映射为name。当然,如果由于某种原因您决定为DataTable列命名的名称与与SQL Server列的名称不同...

答案 4 :(得分:3)

确保在具有get set属性的实体类中添加的列值u也与目标表中存在的顺序相同。

答案 5 :(得分:1)

不会是每个人的解决办法,但这是给我的:

因此,我遇到了这个确切的问题。我似乎遇到的问题是,我的DataTable没有ID列,但目标位置有一个带有主键的列。

当我修改DataTable以使其具有ID时,该副本运行良好。

在我的情况下,Id列对于具有主键并不十分重要,因此我从目标目标表中删除了该列,而SqlBulkCopy可以正常工作。

答案 6 :(得分:0)

当您尝试映射列是字符串长度时,还有另一个问题需要处理 例如TK_NO nvarchar(50)你必须将它映射到              目标字段中的长度相同

答案 7 :(得分:0)

我的问题是列映射而不是值。我正在从开发系统中提取数据,创建目标表,批量复制内容,从生产系统中提取,调整目标表并批量复制内容,因此 2 个批量副本的列顺序不匹配

// explicitly setting the column mapping even though the source & destination column names
// are the same as the column orders will affect the bulk copy giving data conversion errors
foreach (DataColumn column in p_dataTable.Columns)
{
  bulkCopy.ColumnMappings.Add(
    new()
    {
      SourceColumn = column.ColumnName,
      DestinationColumn = column.ColumnName
    }
  );
}

bulkCopy.WriteToServer(p_dataTable);

答案 8 :(得分:-2)

检查您要写入服务器的数据。可能是数据具有未使用的分隔符。

喜欢

045|2272575|0.000|0.000|2013-10-07
045|2272585|0.000|0.000;2013-10-07

您的分隔符是&#39; |&#39; ,但数据的分隔符&#39 ;;&#39; 。 因此,您将收到错误。