Reputation: 19
I'm trying to copy a DataTable with data from a Mysql Table to a Excel file just that simple. But The code that I have creates and fills the data just as it should.
But I'd like to make this code just find the file and update/insert the data in there without the need of opening it.
Another thing, this process is painfully slow, is there a problem with the code ? Or its just as it is ?
The code is:
Dim dts As New DataTable
Dim sql = "SELECT referencia, descricao, notas, tipo, custo, eqs, sac, preco_minimo, percent_pvp, margem_eqs, margem_sac, margem_minimo FROM produtos"
Using conn As New MySqlConnection(ConStrMySql),
cmd As New MySqlCommand(sql, conn)
conn.Open()
Using reader = cmd.ExecuteReader
dts.Load(reader)
End Using
End Using
Dim oXL As Excel.Application
Dim oWB As Excel.Workbook
Dim oSheet As Excel.Worksheet
Dim oRange As Excel.Range
oXL = New Excel.Application
oXL.Visible = True
oXL.DisplayAlerts = False
oWB = oXL.Workbooks.Add
oSheet = DirectCast(oWB.ActiveSheet, Excel.Worksheet)
oSheet.Name = "Produtos"
Dim dt As Data.DataTable = dts
Dim rowCount As Integer = 1
For Each dr As DataRow In dt.Rows
rowCount += 1
For i As Integer = 1 To dt.Columns.Count
If rowCount = 2 Then
Dim t = i - 1
oSheet.Cells(1, i) = dt.Columns(t).ColumnName
End If
Dim j = i - 1
oSheet.Cells(rowCount, i) = dr.Item(j).ToString
Next
Next
oRange = oSheet.Range(oSheet.Cells(1, 1),
oSheet.Cells(rowCount, dt.Columns.Count))
oRange.EntireColumn.AutoFit()
oSheet = Nothing
oRange = Nothing
oWB.SaveAs("test.xlsx")
oWB.Close()
oWB = Nothing
oXL.Quit()
GC.WaitForPendingFinalizers()
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
I've search a little for other ways of filling a file but i only found one that really worked and it was like this:
Using csv As New System.IO.StreamWriter("17 - tic\BD2021-UPDATE.xlsx")
csv.WriteLine(String.Format("{0};{1};{2}", Data1, Data2, Data3))
End Using
But this again creates a new file and i'd like to use an existing one:
Again the problem is: I have a code that works but i cant adapt it to use an existing file without creating a new one each time I call the Sub. And if my code as some problem with its performance.
Thanks
Upvotes: 0
Views: 939
Reputation: 2705
Consider this helper Module
as an alternative to save a Dataset
in a Excel file. You need to have installed on machine/PC Microsoft.Jet.OLEDB.4.0 Excel 8.0 or 12. (If you are working with those files it presumes is installed).
As you see the usage is simple:
Use the following Sub
: ExportDataSetToExcel
pas to it the Dataset
you need to save and the path as String
of file.
If you want to make changes in your Excel doesn’t use the opened Excel file but just make changes in your Dataset
then when you are done you can save the final content as a Excel file (with changes as well)
Module XlsHelper
Event OnTotalRowsDetected(RowCount As Integer)
Event OnRowExported(RowIndex As Integer)
Const CON_STRING As String = "Provider=Microsoft.Jet.OLEDB.4.0; Data Source='{0}'; Extended Properties='Excel 8.0;HDR=YES'"
Function GetConnectionString(fileName As String, mode As String) As String
Dim imex As String = CType(IIf(mode.ToLower = "write", "2", "1"), String)
Return String.Format(CON_STRING, fileName, mode, imex)
End Function
Private Structure ExcelDataTypes
Public Const NUMBER As String = "NUMBER"
Public Const DATETIME As String = "DATETIME"
Public Const TEXT As String = "TEXT"
End Structure
Private Structure NETDataTypes
Public Const _Short As String = "Int16"
Public Const _Int As String = "Int32"
Public Const _Long As String = "Int64"
Public Const _String As String = "String"
Public Const _Date As String = "DateTime"
Public Const _Bool As String = "Boolean"
Public Const _Decimal As String = "Decimal"
Public Const _Double As String = "Double"
Public Const _Float As String = "Float"
End Structure
Sub ExportDataSetToExcel(ByVal dataSet As DataSet, filePath As String)
Dim result() As Byte = Nothing
Try
Dim fileTemp As String = My.Computer.FileSystem.GetTempFileName
If IsNothing(fileTemp) OrElse fileTemp.Length = 0 Then
Throw New Exception("Connot write on temp folder. Check folder permissions")
End If
fileTemp &= ".xls"
If dataSet IsNot Nothing AndAlso dataSet.Tables.Count > 0 Then
Dim sConn As String = GetConnectionString(fileTemp, "Write")
Using connection As OleDbConnection = New OleDbConnection(sConn)
connection.Open()
For Each dt As DataTable In dataSet.Tables
Dim strCreateTableStruct As String = BuildCreateTableCommand(dt)
If String.IsNullOrEmpty(strCreateTableStruct) Then Return
Using command As OleDbCommand = New OleDbCommand(strCreateTableStruct, connection)
command.ExecuteNonQuery()
Dim totRows As Integer = dt.Rows.Count - 1
RaiseEvent OnTotalRowsDetected(totRows)
For rowIndex As Integer = 0 To dt.Rows.Count - 1
Using command1 As OleDbCommand = New OleDbCommand(BuildInsertCommand(dt, rowIndex), connection)
command1.ExecuteNonQuery()
End Using
RaiseEvent OnRowExported(rowIndex)
Next
End Using
Next
End Using
End If
If IO.File.Exists(fileTemp) Then
result = IO.File.ReadAllBytes(fileTemp)
IO.File.Delete(fileTemp)
End If
If result IsNot Nothing AndAlso result.Length > 0 Then
Using Fs As FileStream = New FileStream(filePath, FileMode.OpenOrCreate)
Fs.Write(result, 0, result.Length)
End Using
End If
Catch eX As Exception
MsgBox(eX.ToString)
End Try
End Sub
Private Function BuildCreateTableCommand(ByVal dataTable As DataTable) As String
Dim sb As StringBuilder = New StringBuilder()
Dim dataTypeList As Dictionary(Of String, String) = BuildExcelDataTypes()
If dataTable.Columns.Count <= 0 Then Return Nothing
sb.AppendFormat("CREATE TABLE [{0}] (", BuildExcelSheetName(dataTable))
For Each col As DataColumn In dataTable.Columns
Dim type As String = ExcelDataTypes.TEXT
If dataTypeList.ContainsKey(col.DataType.Name.ToString().ToLower()) Then
type = dataTypeList(col.DataType.Name.ToString().ToLower())
End If
sb.AppendFormat("[{0}] {1},", col.Caption.Replace(" "c, "_"c), type)
Next
sb = sb.Replace(","c, ")"c, sb.ToString().LastIndexOf(","c), 1)
Return sb.ToString()
End Function
Private Function BuildInsertCommand(ByVal dataTable As DataTable, ByVal rowIndex As Integer) As String
Dim sb As StringBuilder = New StringBuilder()
sb.AppendFormat("INSERT INTO [{0}$](", BuildExcelSheetName(dataTable))
For Each col As DataColumn In dataTable.Columns
sb.AppendFormat("[{0}],", col.Caption.Replace(" "c, "_"c))
Next
sb = sb.Replace(","c, ")"c, sb.ToString().LastIndexOf(","c), 1)
sb.Append("VALUES (")
For Each col As DataColumn In dataTable.Columns
Dim type As String = col.DataType.ToString()
Dim strToInsert As String = dataTable.Rows(rowIndex)(col).ToString().Replace("'", "''")
sb.AppendFormat("'{0}',", strToInsert)
Next
sb = sb.Replace(","c, ")"c, sb.ToString().LastIndexOf(","c), 1)
Return sb.ToString()
End Function
Private Function BuildExcelSheetName(ByVal dataTable As DataTable) As String
Dim retVal As String = dataTable.TableName
If dataTable.ExtendedProperties.ContainsKey("test") Then retVal = dataTable.ExtendedProperties("test").ToString()
Return retVal.Replace(" "c, "_"c)
End Function
Private Function BuildExcelDataTypes() As Dictionary(Of String, String)
Dim dataTypeLookUp As Dictionary(Of String, String) = New Dictionary(Of String, String) From {
{NETDataTypes._Short, ExcelDataTypes.NUMBER},
{NETDataTypes._Int, ExcelDataTypes.NUMBER},
{NETDataTypes._Long, ExcelDataTypes.NUMBER},
{NETDataTypes._String, ExcelDataTypes.TEXT},
{NETDataTypes._Date, ExcelDataTypes.DATETIME},
{NETDataTypes._Bool, ExcelDataTypes.TEXT},
{NETDataTypes._Decimal, ExcelDataTypes.NUMBER},
{NETDataTypes._Double, ExcelDataTypes.NUMBER},
{NETDataTypes._Float, ExcelDataTypes.NUMBER}
}
Return dataTypeLookUp
End Function
End Module
Usage:
ExportDataSetToExcel(dts, "C\Somewhere\17 - tic\BD2021-UPDATE.xlsx")
Upvotes: 1