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列と最大1万行を含むことができます。返される列は、サードパーティに送信されるリクエストによって異なります。 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
}
このコードは、宛先フィールドのすべての値をフォーマットする必要があります。このエラー(10進数)の場合、それをクリーンアップする関数は、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)
修正するアイデアはありますか?または詳細情報を得るためのアイデアはありますか?
@Corey-無効な文字をすべて単純に取り除きます。しかし、あなたのコメントは私に答えを考えさせました。
問題は、データベース内のフィールドの多くがNULL値を許可されることでした。 SqlBulkCopyを使用する場合、空の文字列はnull値として挿入されません。そのため、varchar(ビット、整数、10進数、日時など)ではないフィールドの場合、空の文字列を挿入しようとしていました。これは明らかにそのデータ型には無効です。
解決策は、この値を検証するループを変更することでした(文字列ではない各データ型に対して繰り返されます)
//--- 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());
}
上記の調整を行った後、すべてが問題なく挿入されます。
この質問に出くわし、お金ではなく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);
}
}
}
}
}
信じられないので"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テーブルの列が同じ順序であることを確認するか、列名で整列するよう明示的に指定します。
したがって、おそらくこのエラーの原因の大半である列の位置合わせの問題が問題である場合、この答えはあなたのためです。
using System.Data;
//...
myDataTable.Columns.Cast<DataColumn>().ToList().ForEach(x =>
bulkCopy.ColumnMappings.Add(new SqlBulkCopyColumnMapping(x.ColumnName, x.ColumnName)));
これは、作成したBulkCopyオブジェクトに挿入しようとする既存のDataTableを取得し、名前を名前に明示的にマップします。もちろん、何らかの理由で、DataTableの列にSQL Serverの列とは異なる名前を付けることにした場合は...
ターゲットテーブルに存在するのと同じ順序でget setプロパティを持つエンティティクラスに追加された列値uを確認してください。
tK_NO nvarchar(50)のように列の長さを文字列にマッピングしようとすると、別の問題があり、宛先フィールドで同じ長さにマッピングする必要があります