简体   繁体   English

SqlBulkCopy - 来自数据源的 String 类型的给定值无法转换为指定目标列的货币类型

[英]SqlBulkCopy - The given value of type String from the data source cannot be converted to type money of the specified target column

I'm getting this exception when trying to do an SqlBulkCopy from a DataTable.尝试从 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)

I understand what the error is saying, but how I can I get more information, such as the row/field this is happening on?我明白错误在说什么,但我怎样才能获得更多信息,例如发生这种情况的行/字段? The datatable is populated by a 3rd party and can contain up to 200 columns and up to 10k rows.数据表由第 3 方填充,最多可包含 200 列和最多 10k 行。 The columns that are returned depend on the request sent to the 3rd party.返回的列取决于发送给第 3 方的请求。 All of the datatable columns are of string type.所有数据列都是字符串类型。 The columns in my database are not all varchar, therefore, prior to executing the insert, I format the datatable values using the following code (non important code removed):我的数据库中的列并不都是 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
}

This code should format all values for their destination fields.此代码应格式化其目标字段的所有值。 In the case of this error, the decimal, the function that cleans that up will remove any character that is not 0-9 or .在出现此错误的情况下,小数点,清除该错误的函数将删除任何不是 0-9 或 . (decimal point). (小数点)。 This field that is throwing the error would be nullable in the database.引发错误的该字段在数据库中可以为空。

The level 2 exception has this error: 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)

and the level 3 exception has this error:并且 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)

Does anyone have any ideas to fix?有没有人有任何想法要解决? or any ideas to get more info?或任何想法来获得更多信息?

For the people stumbling across this question and getting a similar error message in regards to an nvarchar instead of money:对于那些偶然发现这个问题并收到关于 nvarchar 而不是金钱的类似错误消息的人:

The given value of type String from the data source cannot be converted to type nvarchar of the specified target column.来自数据源的 String 类型的给定值无法转换为指定目标列的 nvarchar 类型。

This could be caused by a too-short column.这可能是由太短的列引起的。

For example, if your column is defined as nvarchar(20) and you have a 40 character string, you may get this error.例如,如果您的列定义为nvarchar(20)并且您有一个 40 个字符的字符串,您可能会收到此错误。

Source资源

Please use SqlBulkCopyColumnMapping.请使用 SqlBulkCopyColumnMapping。

Example:例子:

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";

                    // this is a simpler alternative to explicit column mappings, if the column names are the same on both sides and data types match
                    foreach(DataColumn column in dt.Columns) {
                         s.ColumnMappings.Add(new SqlBulkCopyColumnMapping(column.ColumnName, column.ColumnName));
                     }
                   
                    sqlBulk.WriteToServer(dReader);
                }
            }
        }
    }
}  

Since I don't believe "Please use..." plus some random code that is unrelated to the question is a good answer, but I do believe the spirit was correct, I decided to answer this correctly.由于我不相信"Please use..." plus some random code that is unrelated to the question是一个很好的答案,但我相信精神是正确的,我决定正确回答这个问题。

When you are using Sql Bulk Copy, it attempts to align your input data directly with the data on the server.当您使用 Sql Bulk Copy 时,它会尝试将您的输入数据直接与服务器上的数据对齐。 So, it takes the Server Table and performs a SQL statement similar to this:因此,它获取服务器表并执行类似于以下的 SQL 语句:

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

Therefore, if you give it Columns 1, 3, and 2, EVEN THOUGH your names may match (eg: col1, col3, col2).因此,如果你给它列 1、3 和 2,即使你的名字可能匹配(例如:col1、col3、col2)。 It will insert like so:它将像这样插入:

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

It would be extra work and overhead for the Sql Bulk Insert to have to determine a Column Mapping. Sql Bulk Insert 必须确定列映射将是额外的工作和开销。 So it instead allows you to choose... Either ensure your Code and your SQL Table columns are in the same order, or explicitly state to align by Column Name.因此,它允许您选择...确保您的代码和 SQL 表列的顺序相同,或者明确声明按列名对齐。

Therefore, if your issue is mis-alignment of the columns, which is probably the majority of the cause of this error, this answer is for you.因此,如果您的问题是列未对齐,这可能是导致此错误的主要原因,那么此答案适合您。

TLDR TLDR

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

This will take your existing DataTable, which you are attempt to insert into your created BulkCopy object, and it will just explicitly map name to name.这将采用您现有的 DataTable,您尝试将其插入到您创建的 BulkCopy 对象中,它只会将名称显式映射到名称。 Of course if, for some reason, you decided to name your DataTable Columns differently than your SQL Server Columns... that's on you.当然,如果出于某种原因,您决定将 DataTable 列命名为不同于 SQL Server 列的名称……那就由您决定。

@Corey - It just simply strips out all invalid characters. @Corey - 它只是简单地去除所有无效字符。 However, your comment made me think of the answer.但是,您的评论让我想到了答案。

The problem was that many of the fields in my database are nullable.问题是我的数据库中的许多字段都是可以为空的。 When using SqlBulkCopy, an empty string is not inserted as a null value.使用 SqlBulkCopy 时,不会将空字符串作为空值插入。 So in the case of my fields that are not varchar (bit, int, decimal, datetime, etc) it was trying to insert an empty string, which obviously is not valid for that data type.因此,在我的字段不是 varchar(位、int、十进制、日期时间等)的情况下,它试图插入一个空字符串,这显然对该数据类型无效。

The solution was to modify my loop where I validate the values to this (repeated for each datatype that is not string)解决方案是修改我的循环,在其中验证值(对每个不是字符串的数据类型重复)

//--- 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());
}

After making the adjustments above, everything inserts without issues.进行上述调整后,一切都插入没有问题。

确保您在具有获取设置属性的实体类中添加的列值也以与目标表中存在的顺序相同。

当您尝试映射为字符串长度的列时,您必须注意另一个问题,例如TK_NO nvarchar(50)您必须映射到与目标字段相同的长度。

Not going to be everyone's fix, but it was for me:不会是每个人的解决方案,但它适合我:

So, i ran across this exact issue.所以,我遇到了这个确切的问题。 The problem I seemed to have was when my DataTable didnt have an ID column, but the target destination had one with a primary key.我似乎遇到的问题是当我的 DataTable 没有 ID 列时,但目标目的地有一个带有主键的列。

When i adapted my DataTable to have an id, the copy worked perfectly.当我将我的 DataTable 调整为具有 id 时,副本运行良好。

In my scenario, the Id column isnt very important to have the primary key so i deleted this column from the target destination table and the SqlBulkCopy is working without issue.在我的场景中,Id 列对于拥有主键并不是很重要,因此我从目标目标表中删除了此列,并且 SqlBulkCopy 可以正常工作。

I got the same error "occasionally".我“偶尔”遇到同样的错误。 Note: column mapping was all correct, and that is why code worked most of the time.注意:列映射都是正确的,这就是代码大部分时间都有效的原因。

And I found the root case to be string length issue.我发现根本情况是字符串长度问题。 The target table column had datatype as nvarchar(255) - where as the value being sent was of length more then 255 chars in string.目标表列的数据类型为 nvarchar(255) - 其中发送的值的长度超过 255 个字符的字符串。

Fixed it by increasing column length in DB.通过增加 DB 中的列长度来修复它。

ps: Sadly the error msg wont tell you which column of table is causing this error. ps:遗憾的是,错误消息不会告诉您表的哪一列导致此错误。 That you have to guess/figure it out manually.你必须手动猜测/弄清楚。

My issue was with the column mapping rather than the values.我的问题是列映射而不是值。 I was doing an extract from a dev system, creating the destination table, bulk copying the content, extracting from a prod system, adjusting the destination table and bulk copying the content so the column order from the 2 bulk copies wasn't matching我正在从开发系统中提取,创建目标表,批量复制内容,从产品系统中提取,调整目标表并批量复制内容,因此 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);

short answer: change type from nvarchar(size) to nvarchar(Max) it is issue of string length size简短回答:将类型从 nvarchar(size) 更改为 nvarchar(Max) 这是字符串长度大小的问题

note: all above suggestions made me write this short answer注意:以上所有建议都让我写了这个简短的答案

Check The data you are writing to Server.检查您正在写入服务器的数据。 May be data has delimiter which is not used.可能是数据有未使用的分隔符。

like喜欢

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

your delimiter is '|'你的分隔符是'|' but data has a delimiter ';'但数据有一个分隔符';' . . So for this you are getting the error.所以为此你得到了错误。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 SQlBulkCopy 无法将数据源中 DateTime 类型的给定值转换为指定目标列的 int 类型 - SQlBulkCopy The given value of type DateTime from the data source cannot be converted to type int of the specified target column SqlBulkCopy引发异常:无法将数据源中String类型的给定值转换为指定目标列的bigint类型 - SqlBulkCopy throws exception: The given value of type String from the data source cannot be converted to type bigint of the specified target column 数据源中String类型的给定值无法转换为指定目标列的float类型 - The given value of type String from the data source cannot be converted to type float of the specified target column 来自数据源的 String 类型的给定值无法转换为指定目标列的 nvarchar 类型 - The given value of type String from the data source cannot be converted to type nvarchar of the specified target column 来自数据源的String类型的给定值不能转换为指定目标列的varbinary类型 - The given value of type String from the data source cannot be converted to type varbinary of the specified target column 数据源中String类型的给定值无法转换为指定目标列的int类型 - The given value of type String from the data source cannot be converted to type int of the specified target column 来自数据源的 String 类型的给定值无法转换为指定目标列的 int 类型。 - The given value of type String from the data source cannot be converted to type int of the specified target column.' SQL批量复制“使用ASP.NET无法将数据源中String类型的给定值转换为指定目标列的类型日期时间” - SQL Bulk Copy “The given value of type String from the data source cannot be converted to type datetime of the specified target column” using ASP.NET 在LINQ dat source / gridview中使用文本框值作为参数:“String”类型的值无法转换为“Double”类型 - Using textbox value as parameter in LINQ dat source/gridview : A value of type 'String' cannot be converted to type 'Double' “T”类型的值无法转换为 - Value of type 'T' cannot be converted to
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM