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

Я получаю это исключение при попытке сделать 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)

Я понимаю, что говорит ошибка, но как я могу получить дополнительную информацию, такую как строка/поле, на котором это происходит? Datatable заполняется третьей стороной и может содержать до 200 столбцов и до 10k строк. Возвращаемые столбцы зависят от запроса, отправленного третьей стороне. Все объект DataTable столбцы имеют строковый тип. Этот столбцы в моей базе данных не все varchar, поэтому перед выполнением вставки я форматирую значения datatable, используя следующий код (не важный код удален):

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

этот код нужно форматировать все значения для полей назначения. В случае этой ошибки decimal, функция, которая очищает, что вверх удалит любой символ, который не 0-9 или . (десятичная точка.) Это поле, которое выдает ошибку, будет nullable в базе данных.

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

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

7 ответов


для людей, натыкающихся на этот вопрос и получающих аналогичное сообщение об ошибке в отношении 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";

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

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

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


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


так как я не верю "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, и он будет просто явно сопоставлять имя с именем. Конечно, если по какой-то причине вы решили назвать столбцы DataTable иначе, чем столбцы SQL Server... это твоя вина.


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


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

как

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

ваш разделитель '|' но у данных есть разделитель ';'. Так для этого вы получаете ошибку.