webentwicklung-frage-antwort-db.com.de

SqlBulkCopy - Der angegebene Wert des Typs String aus der Datenquelle kann nicht in den Typ money der angegebenen Zielspalte konvertiert werden

Ich bekomme diese Ausnahme, wenn ich versuche, eine SqlBulkCopy von einer DataTable aus auszuführen.

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)

Ich verstehe, was der Fehler sagt, aber wie kann ich weitere Informationen erhalten, wie z. B. die Zeile/das Feld, in dem sich dies befindet? Die Datentabelle wird von einer dritten Partei gefüllt und kann bis zu 200 Spalten und bis zu 10.000 Zeilen enthalten. Die zurückgegebenen Spalten hängen von der an den Drittanbieter gesendeten Anforderung ab. Alledatatable-Spalten sind vom String-Typ. Die Spalten in meiner Datenbank sind nicht alle varchar. Daher formatiere ich vor dem Einfügen die datierbaren Werte mit dem folgenden Code (nicht wichtiger Code wurde entfernt):

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

Dieser Code sollte alle Werte für ihre Zielfelder formatieren. Im Fall dieses Fehlers, der Dezimalzahl, der Funktion, die das bereinigt, werden alle Zeichen entfernt, die nicht 0-9 oder sind. (Komma). Dieses Feld, das den Fehler auslöst, kann in der Datenbank nullgestellt werden.

Die Ausnahme der Ebene 2 hat diesen Fehler:

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)

und die Ausnahme der Ebene 3 hat diesen Fehler:

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)

Hat jemand irgendwelche Ideen zu beheben? oder irgendwelche Ideen, um weitere Informationen zu erhalten?

33
Ricketts

@Corey - Es werden einfach alle ungültigen Zeichen entfernt. Ihr Kommentar ließ mich jedoch an die Antwort denken.

Das Problem war, dass viele Felder in meiner Datenbank nullfähig sind. Bei Verwendung von SqlBulkCopy wird keine leere Zeichenfolge als Nullwert eingefügt. Bei meinen Feldern, bei denen es sich nicht um varchar handelt (bit, int, decimal, datetime usw.), wurde versucht, einen leeren String einzufügen, der offensichtlich für diesen Datentyp nicht gültig ist.

Die Lösung bestand darin, meine Schleife zu ändern, in der ich die Werte dafür validiere (dies wird für jeden Datentyp wiederholt, der keine Zeichenfolge ist).

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

Nachdem Sie die oben genannten Einstellungen vorgenommen haben, wird alles ohne Probleme eingefügt.

20
Ricketts

Für die Leute, die über diese Frage gestolpert sind und eine ähnliche Fehlermeldung in Bezug auf einen nvarchar anstelle von Geld erhalten:

Der angegebene Wert des Typs String aus der Datenquelle kann nicht in den Typ nvarchar der angegebenen Zielspalte konvertiert werden.

Dies kann durch eine zu kurze Spalte verursacht werden.

Wenn Ihre Spalte beispielsweise als nvarchar(20) definiert ist und Sie eine 40-stellige Zeichenfolge haben, wird diese Fehlermeldung möglicherweise angezeigt. 

Quelle

48
Robotnik

Bitte verwenden Sie SqlBulkCopyColumnMapping.

Beispiel:

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);
                }
            }
        }
    }
}  
32
Joshy Joseph

Da ich nicht glaube, dass "Please use..." plus some random code that is unrelated to the question eine gute Antwort ist, aber ich glaube, dass der Geist richtig war, entschied ich mich, dies richtig zu beantworten.

Wenn Sie Sql Bulk Copy verwenden, wird versucht, Ihre Eingabedaten direkt an den Daten auf dem Server auszurichten. Also nimmt es die Server-Tabelle und führt eine SQL-Anweisung aus, die der folgenden ähnelt:

INSERT INTO [schema].[table] (col1, col2, col3) VALUES

Wenn Sie also die Spalten 1, 3 und 2 angeben, können Ihre Namen sogar übereinstimmen (z. B. col1, col3, col2). Es wird wie folgt eingefügt:

INSERT INTO [schema].[table] (col1, col2, col3) VALUES
                          ('col1', 'col3', 'col2')

Es wäre eine zusätzliche Arbeit und ein zusätzlicher Aufwand, wenn die SQL-Masseneinfügung eine Spaltenzuordnung ermitteln muss. Stattdessen können Sie wählen ... Wählen Sie entweder aus, dass der Code und die Spalten Ihrer SQL-Tabelle in der gleichen Reihenfolge sind, oder geben Sie explizit an, dass Sie den Spaltennamen angeben.

Wenn Ihr Problem also eine falsche Ausrichtung der Spalten ist, was wahrscheinlich die Hauptursache für diesen Fehler ist, ist diese Antwort für Sie.

TLDR

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

Dadurch wird Ihre vorhandene DataTable übernommen, die Sie in Ihr erstelltes BulkCopy-Objekt einfügen möchten, und der Name wird lediglich explizit dem Namen zugeordnet. Natürlich, wenn Sie sich aus irgendeinem Grund dafür entschieden haben, Ihre DataTable-Spalten anders als Ihre SQL Server-Spalten zu benennen.

9
Suamere

Stellen Sie sicher, dass die in Entitätsklasse hinzugefügten Spaltenwerte u die gesetzten Eigenschaften in derselben Reihenfolge wie in der Zieltabelle haben.

3
santhoshraj

es gibt ein weiteres Problem, das Sie beachten müssen, wenn Sie die Zuordnungsspalte versuchen. Die Länge der Zeichenfolge Zum Beispiel TK_NO nvarchar (50) müssen Sie es auf .__ zuordnen. die gleiche Länge im Zielfeld

0
Medhat Makram