68

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 列と最大 10,000 行を含めることができます。返される列は、サードパーティに送信されたリクエストによって異なります。すべてのデータテーブル列は文字列型です。私のデータベースの列はすべて 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)

誰にも修正するアイデアはありますか?またはより多くの情報を得るためのアイデアはありますか?

4

10 に答える 10

42

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);
                }
            }
        }
    }
}  
于 2014-05-08T19:57:28.327 に答える
34

良い答えだとは思いません"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 が列マッピングを決定しなければならないのは、余分な作業とオーバーヘッドになります。したがって、代わりに選択できます...コードとSQ​​Lテーブルの列が同じ順序であることを確認するか、列名で整列するように明示的に指定します。

したがって、問題が列の位置合わせの誤りである場合、これがおそらくこのエラーの原因の大部分であると思われます。この回答はあなたのためのものです。

TLDR

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

これは、作成した BulkCopy オブジェクトに挿入しようとしている既存の DataTable を取得し、名前を名前に明示的にマップします。もちろん、何らかの理由で DataTable 列に SQL Server 列とは異なる名前を付けることにした場合は、それはあなた次第です。

于 2018-06-22T20:31:40.023 に答える
24

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

上記の調整を行った後、すべてが問題なく挿入されます。

于 2013-08-09T14:36:07.850 に答える
6

エンティティ クラスに追加された列の値が、ターゲット テーブルに存在するのと同じ順序でプロパティが設定されていることを確認してください。

于 2016-09-22T05:46:37.750 に答える
-3

サーバーに書き込むデータをチェックします。データに使用されていない区切り文字がある可能性があります。

お気に入り

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

区切り文字は「|」です ただし、データには区切り文字「;」があります . したがって、このためにエラーが発生します。

于 2015-05-28T07:38:13.893 に答える