SqlBulkCopy - данное значение типа String из источника данных не может быть преобразовано в тип деньги указанного целевого столбца.

Я получаю это исключение при попытке выполнить SqlBulkCopy из DataTable.

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 столбцов и до 10 тыс. Строк. Возвращаемые столбцы зависят от запроса, отправленного третьей стороне. Все столбцы datatable имеют строковый тип. Не все столбцы в моей базе данных являются 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 или. (десятичная точка). Это поле, которое вызывает ошибку, будет иметь значение NULL в базе данных.

Исключение уровня 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)

Есть ли у кого-нибудь идеи исправить? или есть идеи, чтобы получить больше информации?


person Ricketts    schedule 09.08.2013    source источник


Ответы (9)


Для людей, которые наткнулись на этот вопрос и получили аналогичное сообщение об ошибке в отношении nvarchar вместо денег:

Данное значение типа String из источника данных не может быть преобразовано в тип nvarchar указанного целевого столбца.

Это могло быть вызвано слишком коротким столбцом.

Например, если ваш столбец определен как nvarchar(20) и у вас есть строка из 40 символов, вы можете получить эту ошибку.

Источник

person Robotnik    schedule 10.03.2014

Пожалуйста, используйте 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);
                }
            }
        }
    }
}  
person Joshy Joseph    schedule 08.05.2014
comment
Если вы посмотрите на пример кода вопроса, ColumnMappings устанавливаются. Это не имело ничего общего с отображением столбцов, а скорее с форматом данных, поступающих в эти столбцы. - person Ricketts; 09.05.2014
comment
Хотя они говорят, что SqlBulkCopy автоматически сопоставляет эти столбцы, на самом деле вам действительно нужно указать их, иначе вы получите эти типы ошибок. По крайней мере, это сработало для меня. - person Micro; 13.11.2014
comment
Раньше я использовал автоматическое сопоставление, но я столкнулся с той же проблемой с bigint (а не с varchar) в наборе данных с почти 120 столбцами. Простое явное сопоставление столбцов разрешило это. Иди разберись. - person DotThoughts; 13.05.2016
comment
Это сработало для меня. Я использовал метод расширения ToDataTable () из другого ответа, но это не помогло. Причина сбоя заключалась в том, что порядок столбцов в базе данных отличался от порядка свойств в классе. - person Chris; 06.07.2016
comment
У меня был столбец Guid в моей таблице данных и в таблице базы данных. Все столбцы в DataTable и DB были названы одинаково. Я получил аналогичную ошибку в методе WriteToServer (данное значение типа Guid из источника данных не может быть преобразовано в тип int указанного целевого столбца). Ошибка исчезла, когда я явно добавил все сопоставления столбцов. - person Mark Hagers; 28.12.2016
comment
У меня была проблема с другим типом данных преобразования, и настройка сопоставлений устранила ее для меня, хотя в моем порядке столбцов или типах данных не было ничего плохого. - person Secret Squirrel; 28.04.2017
comment
У меня также были случайные ошибки, пока я вручную не указал сопоставление столбцов. В моем случае, поскольку я уже настроил столбцы таблицы массового копирования вручную, я мог автоматически создать сопоставления: foreach (столбец DataColumn в bulkCopyTable.Columns) {sqlBulkCopy.ColumnMappings.Add (новый SqlBulkCopyColumnMapping (column.ColumnName, column.ColumnName, column.ColumnName )); } - person dbruning; 12.07.2017
comment
Отлично, это проблема для меня! - person HerrimanCoder; 19.10.2020

Поскольку я не считаю "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 находятся в одном порядке, либо явно укажите для выравнивания по имени столбца.

Поэтому, если ваша проблема заключается в неправильном выравнивании столбцов, что, вероятно, является основной причиной этой ошибки, этот ответ для вас.

TL; DR

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

Это займет ваш существующий DataTable, который вы пытаетесь вставить в созданный вами объект BulkCopy, и он просто явно сопоставит имя с именем. Конечно, если по какой-то причине вы решили назвать свои столбцы DataTable иначе, чем столбцы SQL Server ... это зависит от вас.

person Suamere    schedule 22.06.2018
comment
Тот! Большое Вам спасибо. Это объяснило множество моих проблем. - person trailmax; 11.10.2018
comment
Я получал аналогичное исключение, и даже выравнивание свойств моего класса по таблице не разрешило его. Отличное решение! - person David Peters; 15.01.2020
comment
DataTable.Columns не имеет метода Cast - person JWiley; 29.07.2020
comment
@JWiley Чтобы увидеть расширение Cast, вы должны быть пользователем LINQ. Чтобы включить функциональность LINQ, введите using System.Linq; в верхней части файла. Или напишите вызов .Columns.Cast<..., получите сообщение об ошибке, поместите курсор на ошибку и используйте ярлык Ctrl+. Control Period (по умолчанию). Это должно включать необходимые базовые библиотеки. - person Suamere; 30.07.2020

@Corey - он просто удаляет все недопустимые символы. Однако ваш комментарий заставил меня задуматься над ответом.

Проблема заключалась в том, что многие поля в моей базе данных допускают значение NULL. При использовании SqlBulkCopy пустая строка не вставляется как значение NULL. Итак, в случае моих полей, которые не являются 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());
}

После внесения вышеуказанных корректировок все вставляется без проблем.

person Ricketts    schedule 09.08.2013

Убедитесь, что значения столбца u, добавленные в класс сущности, для которого установлены свойства, также находятся в том же порядке, что и в целевой таблице.

person santhoshraj    schedule 22.09.2016
comment
Почему это было отклонено? У меня возникла проблема с этим исключением из-за неправильного порядка столбцов. - person Nicklas Møller Jepsen; 13.01.2017

Есть еще одна проблема, о которой вам нужно позаботиться, когда вы пытаетесь сопоставить столбец с длиной строки, например TK_NO nvarchar(50), вам нужно будет сопоставить ту же длину, что и поле назначения.

person Medhat Makram    schedule 21.03.2018
comment
Это просто качественный ответ - person Mathews Sunny; 21.03.2018

Не для всех, но это было для меня:

Итак, я столкнулся с этой проблемой. Проблема, с которой я столкнулся, заключалась в том, что в моем DataTable не было столбца идентификатора, но у целевого пункта назначения был столбец с первичным ключом.

Когда я адаптировал свой DataTable для идентификатора, копия работала отлично.

В моем сценарии столбец Id не очень важен для наличия первичного ключа, поэтому я удалил этот столбец из целевой целевой таблицы, и SqlBulkCopy работает без проблем.

person Ashetynw    schedule 06.11.2019

Моя проблема заключалась в сопоставлении столбцов, а не в значениях. Я делал извлечение из системы разработки, создавая целевую таблицу, массовое копирование контента, извлечение из производственной системы, настройку целевой таблицы и массовое копирование содержимого, чтобы порядок столбцов из двух массовых копий не соответствовал

// 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);
person Pierre Collard Suero    schedule 18.05.2021

Отметьте данные, которые вы записываете на сервер. Возможно, в данных есть неиспользуемый разделитель.

как

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

ваш разделитель - '|', но данные имеют разделитель ';'. Итак, вы получаете сообщение об ошибке.

person Md Kauser Ahmmed    schedule 28.05.2015