9

I have a DBF file which I am trying to import and then write that to a SQL table. The issue I am running into is if I use SqlBulkCopy, it requires me to create the table in advance but that is not possible in my scenario as the dbf file changes constantly.

Here is my code so far:

public void saveDBF()
        {

            //define the connections to the .dbf file
            OleDbConnection oConn = new OleDbConnection(@"Provider=Microsoft.Jet.OLEDB.4.0; Data Source="+ Path.GetDirectoryName(tbFile.Text)+";Extended Properties=dBase III");

            OleDbCommand command = new OleDbCommand("select * from " + Path.GetFileName(tbFile.Text), oConn);

            //open the connection and read in all the airport data from .dbf file into a datatable

            oConn.Open();
            DataTable dt = new DataTable();

            dt.Load(command.ExecuteReader());

            oConn.Close();  //close connection to the .dbf file

            //create a reader for the datatable
            DataTableReader reader = dt.CreateDataReader();

            myConnection = new SqlConnection(cString);
            myConnection.Open();   ///this is my connection to the sql server
            SqlBulkCopy sqlcpy = new SqlBulkCopy(myConnection);

            sqlcpy.DestinationTableName = "TestDBF";  //copy the datatable to the sql table

            sqlcpy.WriteToServer(dt);

            myConnection.Close();

            reader.Close();

        }

It keeps failing at sqlcpy.WriteToServer(dt); stating it cannot access the destination table.

Is there an option in C# to create the table on the fly before writing to that table?

Si8
  • 9,141
  • 22
  • 109
  • 221

1 Answers1

20

This method can help you:

static void AutoSqlBulkCopy(DataSet dataSet)
{
    var sqlConnection = new SqlConnection("Data Source=sqlServer;Initial Catalog=mydatabase;user id=myuser;password=mypass;App=App");
    sqlConnection.Open();
    foreach (DataTable dataTable in dataSet.Tables)
    {
        // checking whether the table selected from the dataset exists in the database or not
        var checkTableIfExistsCommand = new SqlCommand("IF EXISTS (SELECT 1 FROM sysobjects WHERE name =  '" + dataTable.TableName + "') SELECT 1 ELSE SELECT 0", sqlConnection);
        var exists = checkTableIfExistsCommand.ExecuteScalar().ToString().Equals("1");

        // if does not exist
        if (!exists)
        {
            var createTableBuilder = new StringBuilder("CREATE TABLE [" + dataTable.TableName + "]");
            createTableBuilder.AppendLine("(");

            // selecting each column of the datatable to create a table in the database
            foreach (DataColumn dc in dataTable.Columns)
            {
                createTableBuilder.AppendLine("  ["+ dc.ColumnName + "] VARCHAR(MAX),");
            }

            createTableBuilder.Remove(createTableBuilder.Length - 1, 1);
            createTableBuilder.AppendLine(")");

            var createTableCommand = new SqlCommand(createTableBuilder.ToString(), sqlConnection);
            createTableCommand.ExecuteNonQuery();
        }

        // if table exists, just copy the data to the destination table in the database
        // copying the data from datatable to database table
        using (var bulkCopy = new SqlBulkCopy(sqlConnection))
        {
            bulkCopy.DestinationTableName = dataTable.TableName;
            bulkCopy.WriteToServer(dataTable);
        }
    }
}

And you can use it like this:

var ds = new DataSet("MyDataSet");
var dt = new DataTable("MyDataTable");
dt.Columns.Add(new DataColumn("name", typeof(string)));
dt.Columns.Add(new DataColumn("email", typeof(string)));
dt.Columns.Add(new DataColumn("phone", typeof(string)));

dt.Rows.Add("John","john@company.com","56765765");
dt.Rows.Add("Tom","tom@company.com","8978987987");
ds.Tables.Add(dt);
AutoSqlBulkCopy(ds);
TotPeRo
  • 6,561
  • 4
  • 47
  • 60
  • So add that before my `sqlcopy.WriteToServer(dt);` line? – Si8 Jun 04 '14 at 20:25
  • 1
    @SiKni8 yes but adapt the code from your.... for example replace the name of the connection sqlcon->myConnection ...etc. – TotPeRo Jun 04 '14 at 20:29
  • Thanks. For this line `SqlCommand cmd = new SqlCommand("SELECT * FROM " + tbTableName.Text + " where name = '" + dt.TableName + "'", myConnection);` is it correct? I am allowing the user to enter the TABLE name they would like to save the data to. Or should it be `SqlCommand cmd = new SqlCommand("SELECT * FROM " + tbTableName.Text + " where name = '" + tbTableName.Text + "'", myConnection);` – Si8 Jun 04 '14 at 20:33
  • After looking through the code, it seems I have to replace all the `dt.TableName` to `tbTableName.Text` to allow the user to enter a table name? Correct? – Si8 Jun 04 '14 at 20:35
  • @SiKni8 yes if the you don't want to use the Data Table name and you want to add custom table name. – TotPeRo Jun 04 '14 at 20:43
  • Thank you. Do I still have to do ColumnMappings? – Si8 Jun 04 '14 at 20:50
  • No because the names from the data table is the same from the database table – TotPeRo Jun 04 '14 at 20:53
  • I am just a bit confused as to what should be the `DataTable dt in ds.Tables` for my code? – Si8 Jun 04 '14 at 20:53
  • I Upvoted because it is something that will most likely end up being my solution after I fix up the nooks and crannies of the code :) – Si8 Jun 04 '14 at 21:04
  • comment or remove this foreach ... this help you if you have two or more table into dataset – TotPeRo Jun 04 '14 at 21:04
  • Awesome it worked!!!! :) I wonder how can I get the forloop working so in case I do have multiple in the future I can use the existing code. – Si8 Jun 04 '14 at 21:10
  • 1
    you can send some parameters to saveDBF() function and you can use this function in future – TotPeRo Jun 04 '14 at 21:21
  • No need for the if/else, you are writing the same code twice. DRY. Simply end the if and write the Bulkcopy code after it, its the same code no matter what. – MattE Feb 24 '19 at 16:31
  • 3
    What about using dc.DataType as column datatype instead of VARCHAR(MAX)? – Iftikhar Aug 07 '19 at 13:41