簡體   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

嘗試從 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)

我明白錯誤在說什么,但我怎樣才能獲得更多信息,例如發生這種情況的行/字段? 數據表由第 3 方填充,最多可包含 200 列和最多 10k 行。 返回的列取決於發送給第 3 方的請求。 所有數據列都是字符串類型。 我的數據庫中的列並不都是 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)

有沒有人有任何想法要解決? 或任何想法來獲得更多信息?

對於那些偶然發現這個問題並收到關於 nvarchar 而不是金錢的類似錯誤消息的人:

來自數據源的 String 類型的給定值無法轉換為指定目標列的 nvarchar 類型。

這可能是由太短的列引起的。

例如,如果您的列定義為nvarchar(20)並且您有一個 40 個字符的字符串,您可能會收到此錯誤。

資源

請使用 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";

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

由於我不相信"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 Bulk Insert 必須確定列映射將是額外的工作和開銷。 因此,它允許您選擇...確保您的代碼和 SQL 表列的順序相同,或者明確聲明按列名對齊。

因此,如果您的問題是列未對齊,這可能是導致此錯誤的主要原因,那么此答案適合您。

TLDR

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

這將采用您現有的 DataTable,您嘗試將其插入到您創建的 BulkCopy 對象中,它只會將名稱顯式映射到名稱。 當然,如果出於某種原因,您決定將 DataTable 列命名為不同於 SQL Server 列的名稱……那就由您決定。

@Corey - 它只是簡單地去除所有無效字符。 但是,您的評論讓我想到了答案。

問題是我的數據庫中的許多字段都是可以為空的。 使用 SqlBulkCopy 時,不會將空字符串作為空值插入。 因此,在我的字段不是 varchar(位、int、十進制、日期時間等)的情況下,它試圖插入一個空字符串,這顯然對該數據類型無效。

解決方案是修改我的循環,在其中驗證值(對每個不是字符串的數據類型重復)

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

進行上述調整后,一切都插入沒有問題。

確保您在具有獲取設置屬性的實體類中添加的列值也以與目標表中存在的順序相同。

當您嘗試映射為字符串長度的列時,您必須注意另一個問題,例如TK_NO nvarchar(50)您必須映射到與目標字段相同的長度。

不會是每個人的解決方案,但它適合我:

所以,我遇到了這個確切的問題。 我似乎遇到的問題是當我的 DataTable 沒有 ID 列時,但目標目的地有一個帶有主鍵的列。

當我將我的 DataTable 調整為具有 id 時,副本運行良好。

在我的場景中,Id 列對於擁有主鍵並不是很重要,因此我從目標目標表中刪除了此列,並且 SqlBulkCopy 可以正常工作。

我“偶爾”遇到同樣的錯誤。 注意:列映射都是正確的,這就是代碼大部分時間都有效的原因。

我發現根本情況是字符串長度問題。 目標表列的數據類型為 nvarchar(255) - 其中發送的值的長度超過 255 個字符的字符串。

通過增加 DB 中的列長度來修復它。

ps:遺憾的是,錯誤消息不會告訴您表的哪一列導致此錯誤。 你必須手動猜測/弄清楚。

我的問題是列映射而不是值。 我正在從開發系統中提取,創建目標表,批量復制內容,從產品系統中提取,調整目標表並批量復制內容,因此 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);

簡短回答:將類型從 nvarchar(size) 更改為 nvarchar(Max) 這是字符串長度大小的問題

注意:以上所有建議都讓我寫了這個簡短的答案

檢查您正在寫入服務器的數據。 可能是數據有未使用的分隔符。

喜歡

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

你的分隔符是'|' 但數據有一個分隔符';' . 所以為此你得到了錯誤。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM