C# Export mit OleDb in eine CSV Datei... Problemchen

roker002

Commander
Registriert
Dez. 2007
Beiträge
2.061
Hallo,

ich habe ein Problem. Ich möchte gerne eine Prozedur für MS SQL schreiben, die mir beliebige Tabelle in eine CSV Datei exportiert.

Der ganzer Script ist vorbereitet. Das Problem ist, ich bekomme immer wieder Exception beim Erstellen der Datei mittels "Create Table test.csv".
Für Testcases have ich sqlcontext umkehren lassen.
Code:
[Microsoft.SqlServer.Server.SqlProcedure(Name = "ext_export_csv")]
public static SqlInt64 ExportTable(SqlString fullTableName, SqlString fullPath)
{
    var fi = new FileInfo(fullPath.Value);
    fi.Refresh();
    if (fi.Directory.Exists==false)
        throw new DirectoryNotFoundException(String.Concat("Directory: ", fullPath.Value, " not found."));
    String folder = fi.DirectoryName,
            file = fi.Name;

    List<String> columns = new List<String>(),
                    values = new List<String>();
    var param  = new Dictionary<String, System.Data.OleDb.OleDbParameter>();
    SqlInt64 result = new SqlInt64();
    if (SqlContext.IsAvailable == false)
    {
        var csb = new SqlConnectionStringBuilder();
        //csb.ContextConnection = true;
        csb.DataSource = "localhost";
        csb.IntegratedSecurity = true;
        DataSet ds = new DataSet();
        using (var connection = new SqlConnection(csb.ConnectionString))
        {
            connection.Open();
            using (var command = new SqlCommand("SELECT * FROM " + fullTableName.Value, connection))
            {
                using (var da = new SqlDataAdapter(command))
                {
                    da.Fill(ds);
                }
            }
        }
        var columnList = ds.Tables[0].Columns.OfType<DataColumn>().ToList();
        columnList.ForEach(a => { columns.Add(a.ColumnName); values.Add(String.Concat("@", a.ColumnName)); });
        csb = null;
        /// Try to find Provider on the system.
        String oleDbProvider = "Microsoft.Jet.OLEDB.4.0";
        Boolean providerFound = false;
        using (var reader = System.Data.OleDb.OleDbEnumerator.GetRootEnumerator())
        {
            while (reader.Read())
            {
                if (String.Equals(reader.GetValue(0).ToString(), oleDbProvider))
                {
                    providerFound = true;
                    break;
                }
            }
        }
        if (providerFound == false)
            throw new ArgumentException(String.Concat("Could not find \"", oleDbProvider, "\" provider"));
        Boolean prepared = false;
        System.Data.OleDb.OleDbParameter current = null;
        var oledbconn = new System.Data.OleDb.OleDbConnectionStringBuilder();
        oledbconn.Provider = oleDbProvider;
        oledbconn.DataSource = folder;
        oledbconn.Add("Extended Properties", "text");
        using (var connection = new System.Data.OleDb.OleDbConnection(oledbconn.ConnectionString))//String.Concat("Provider=", oleDbProvider, ";Extended Properties='text;HDR=Yes;FMT=Delimited';Data Source=", folder, ";")
        {
            connection.Open();
            using (var cmd = new System.Data.OleDb.OleDbCommand("CREATE TABLE " + file + " (name char(30))", connection))
            {
                cmd.ExecuteNonQuery(); //[COLOR="#FF0000"]HIER FEHLER[/COLOR]
                cmd.CommandText = String.Concat("INSERT INTO ", file, " (", String.Join(", ", columns.ToArray()), ") VALUES(", String.Join(", ", values.ToArray()), ")");
                foreach (DataRow r in ds.Tables[0].Rows)
                {
                    foreach (var c in columnList)
                    {
                        if (param.TryGetValue(c.ColumnName, out current))
                        {
                            current.Value = r[c];
                        }
                        else
                        {
                            /*
                            current = cmd.Parameters.AddWithValue("@" + c.ColumnName, r[c]);
                            current.OleDbType = System.Data.OleDb.OleDbType.LongVarWChar;
                            current.IsNullable = true;
                            current.Size = 1000;
                            param.Add(c.ColumnName, current);*/
                                    
                            current = new System.Data.OleDb.OleDbParameter();
                            current.ParameterName = "@" + c.ColumnName;
                            current.Value = r[c];
                            current.IsNullable = true;
                            //current.OleDbType = System.Data.OleDb.OleDbType.LongVarWChar;
                            param.Add(c.ColumnName, current);
                                     
                        }
                    }

                    if (prepared == false)
                    {
                        cmd.Prepare();
                        prepared = true;
                    }
                    cmd.ExecuteNonQuery();
                }
            }
        }
    }
    else
        throw new Exception("No Sql Context found.");
    return result;
}

Der Testcase sieht so aus.

Code:
        /// <summary>
        ///Ein Test für "ExportTable"
        ///</summary>
        [TestMethod()]
        public void ExportTableTest()
        {
            SqlString fullTableName = "test.dbo.test"; // TODO: Passenden Wert initialisieren
            SqlString fullPath = @"c:\test.csv"; // TODO: Passenden Wert initialisieren
            Misc.ExportTable(fullTableName, fullPath);
        }


Also mir geht es vor allem nicht um die Parameter... bis dahin kommt man mit dem debugger nicht. Ich bekomme die sch***ß Tabelle nicht erstellt.

Es gibt noch ein Bild vom Debug.

Und ja, es soll möglich sein in eine CSV Datei zu schreiben.
Und ja der Code ist noch nicht vollständig, da im Create nur eine Spalte steht. Wird später auf dynamisch Spalten umgesetzt.
 

Anhänge

  • test.jpg
    test.jpg
    149,9 KB · Aufrufe: 341
Was hat dein ganzes Vorhaben mit einer SQL Procedure zu tun? Mir ist auch nicht ganz klar warum du zuerst irgendwelche Data Sets füllst. Öffne doch einfach die Verbindung auf die von dir gewollte Tabelle und gehe mit einer Schleife dann über alle Einträge durch.

Zeig uns doch mal wie der Befehl cmd.CommandText ausgeschrieben heißt, wenn er auf den Fehler läuft (einfach komplett als Exception ausgeben).
 
Also die Exception ist:
Text file specification field separator matches decimal separator or text delimiter.
ErrorCode:

Ich habe danach gesucht, aber ich konnte nicht finden, was es mit Create Table zutun haben sollte.

using (var cmd = new System.Data.OleDb.OleDbCommand("CREATE TABLE " + file + " (name char(30))", connection))

Wieso ich das ganze über DataSet Fetche:
Ich will dynamisch die CSV Dateien erstellen mit den entsprechenden Spalten und deren Datentypen und mit Header Teil.

So kann man x-beliebige Tabelle in eine CSV Datei exportieren ohne was dazu zu programmieren. UND ich muss mich nicht um Feldformatierung kümmern.
 
Bitte den SQL Befehl ausgeben, den du da absetzt. Die Fehlermeldung "Text file specification field separator matches decimal separator or text delimiter." sagt jetzt erst einmal aus, dass du ein Zeichen als Textseperator einsetzt, welches das OS als Dezimaltrennzeichen versteht. Das ist also eine Frage der Spracheinstellungen oder der falschen Verwendung von dir im Code. Warum das bei einer SQL Abfrage kommt, ist sowieso noch ein anders Thema.

Deswegen erst einmal logisch und Schritt für Schritt: Geben wir mal aus, was du im Detail bei
cmd.CommandText = String.Concat("INSERT INTO ", file, " (", String.Join(", ", columns.ToArray()), ") VALUES(", String.Join(", ", values.ToArray()), ")");
absetzt.

Es interessiert, was du da zusammen baust. Du könntest also ganz einfach ein
throw (cmd.CommandText = String.Concat("INSERT INTO ", file, " (", String.Join(", ", columns.ToArray()), ") VALUES(", String.Join(", ", values.ToArray()), ")"));
ausgeben. Da muss auch keine weitere Logik rein, da du den Textinhalt nur einmal durchläufst? Ansonsten baue eine Logik ein, dass die ausgegebene Fehlermeldung den Wert von cmd.CommandText entspricht.
 
es kommt erst garnicht zu einem Insert. Der bleibt beim CREATE TABLE hängen. Deswegen verstehe ich die Fehlermeldung nicht.

Ist es möglich das man Schema.ini in dem gleichen Verzeichnis haben muss wo man gerade CREATE TABLE ausführt? Ich habe auch mit Schema.ini Datei versucht aber ohne erfolg.
Irgendwie ist es frustrierend.
Danke überigens für die Antwort!
 
Hmm. Ok.

Dann gib doch den Wert von ("CREATE TABLE " + file + " (name char(30))") aus.
 
Zurück
Oben