Better way than this to export Datatable to Excel/Email?
In the code below, I retrieve a DataTable from SQL, then enumerate into a text file, attach it to an email and send it:
Dim dt as DataTable = GetData()Dim sb As New StringBuildersb.Append("<html>")sb.Append("<body>")sb.Append("<table>")sb.Append("<tr>")For Each dc As DataColumn In dt.Columns sb.Append("<td>" & dc.ColumnName & "</td>")Nextsb.AppendLine("</tr>")For Each dr As DataRow In dt.Rows sb.Append("<tr>") For Each dc As DataColumn In dt.Columns sb.Append("<td>" & dr.Item(dc.ColumnName).ToString & "</td>") Next sb.Append("</tr>")Nextsb.Append("</table>")sb.Append("</body>")sb.Append("</html>")Dim mm As New MailMessagemm.To.Add(New MailAddress("Me@MyDomain.com", "Me"))mm.From = New MailAddress("some@body.com")mm.IsBodyHtml = Falsemm.Body = "Your data is attached"If File.Exists(Environment.CurrentDirectory & "\" & Me._SpreadSheetFile) Then File.Delete(Environment.CurrentDirectory & "\" & Me._SpreadSheetFile)End IfDim fs As FileStream = File.Create((Environment.CurrentDirectory & "\" & Me._SpreadSheetFile))fs.Close()fs.Dispose()Dim writer As New StreamWriter((Environment.CurrentDirectory & "\" & Me._SpreadSheetFile), True)writer.WriteLine(sb.ToString)writer.Close()writer.Dispose()Try Dim att As New Attachment(Environment.CurrentDirectory & "\" & Me._SpreadSheetFile) att.ContentType = New Net.Mime.ContentType("application/vnd.ms-excel") mm.Attachments.Add(att)Catch ex As ApplicationException Throw exEnd TryDim sc As New SmtpClient(System.Environment.MachineName, 25)sc.DeliveryMethod = Net.Mail.SmtpDeliveryMethod.Networksc.UseDefaultCredentials = Falsesc.Credentials = Me._emailCredentialsTry sc.Send(mm)Catch ex As Exception Throw exFinally sb = Nothing mm = Nothing sc = NothingEnd Trydt.Dispose()
I am a VB.NET newbie, so have the following questions:
1) The attachment should be a Microsoft Excel spreadsheet. Because Excel opens basic HTML, I've simply create a text file with HTML inside, and saved as XLS. Is there a more efficient/better recommended way to export to Excel? I've read dozens of tutorials on this today already so please don't post any more...!
2) Is it possible to attach the file directly from memory, to save creating the file in the operating system, then attaching it, then deleting next time it is executed?
3) At the risk of scrapping all my code, is there a more efficient/faster way to complete my logic?
Visual Basic.NET.NET Programming
Last Comment
Rouchie
8/22/2022 - Mon
YZlat
this procedure will write your datatable to a specified excel file
Public Sub DataTableToExcel(ByVal fileName As String, ByVal dt As System.Data.DataTable) Dim ExlApp As Excel.Application Dim aBook As Excel.Workbook Dim iCol, iRow, iColVal As Integer Dim missing As Object = System.Reflection.Missing.Value Dim i As Integer ' Open the document that was chosen by the dialog Dim aBook As Excel.Workbook Try ''re-initialize excel app ExlApp = New Excel.Application If ExlApp Is Nothing Then ''throw an exception Throw (New Exception("Unable to Start Microsoft Excel")) Else ''supresses overwrite warnings ExlApp.DisplayAlerts = False 'aBook = New Excel.Workbook ''check if file exists If File.Exists(fileName) Then aBook = ExlApp.Workbooks.Open(fileName) Else aBook = ExlApp.Workbooks.Add(Excel.XlWBATemplate.xlWBATWorksheet) End If With ExlApp .SheetsInNewWorkbook = 1 '.Workbooks.Add() .Worksheets(1).Select() 'For displaying the column name in the the excel file. For iCol = 0 To dt.Columns.Count - 1 ''clear column name before setting a new value .Cells(1, iCol + 1).Value = "" .Cells(1, iCol + 1).Value = dt.Columns(iCol).ColumnName.ToString Next 'For displaying the column value row-by-row in the the excel file. For iRow = 0 To dt.Rows.Count - 1 Try For iColVal = 0 To dt.Columns.Count - 1 If TypeOf dt.Rows(iRow).ItemArray(iColVal) Is String Then .Cells(iRow + 2, iColVal + 1).Value = "'" & Trim(dt.Rows(iRow).ItemArray(iColVal).ToString) Else .Cells(iRow + 2, iColVal + 1).Value = Trim(dt.Rows(iRow).ItemArray(iColVal).ToString) End If Next Catch ex As Exception err.WriteError("C:\ERRORS\", ex.Message, ex.StackTrace) 'err.WriteError(GetExecutingAssembly.Location.ToString() & "errorlog.txt", ex.Message, ex.StackTrace) Console.Write("ERROR: " & ex.Message) End Try Next If File.Exists(fileName) Then .ActiveWorkbook().Save() 'fileName) Else .ActiveWorkbook().SaveAs(fileName, missing, missing, missing, missing, missing, Excel.XlSaveAsAccessMode.xlNoChange, missing, missing, missing, missing, missing) End If .ActiveWorkbook.Close() End With Console.Write("File exported sucessfully") End If Catch ex As Runtime.InteropServices.COMException Console.Write("ERROR: " & ex.Message) Catch ex As Exception Console.Write("ERROR: " & ex.Message) Finally ExlApp.Quit() System.Runtime.InteropServices.Marshal.ReleaseComObject(ExlApp) aBook = Nothing ExlApp = Nothing End Try End Sub
Excel can connect directly to SQL Server and create a spreadsheet out of the result of a query.
You might be able to have Excel do all the job for your without having to go through a DataTable to retrieve the data.
Explore the Data...Import External Data menu entry in Excel 2003 or whatever ribbon entry that replaces that in newer versions.
Using that feature, your .NET code could simply open the Excel file, trigger a refresh of the data, save and the job is done. Simply send the result.
I have not worked with that feature in years, and used the scenario that I am suggesting from VB6 with old code I do not have anymore, so I cannot send you sample code. But I remember that it was very efficient and easy to code.
Rouchie
ASKER
Thanks for your responses. Please let me answer each in turn...
>> this procedure will write your datatable to a specified excel file
Yes that looks fine. Some questions though:
1) I assume I need to create a Reference to Excel in the VB project for this to work?
2) Excel is not installed on the live server, only on development machines, so would this work in a live environment?
3) You appear to enumerate row-by-row, as in my example, so surely its not any more efficient?
4) Its also creating a file on the OS which I'm trying to avoid - can the file be passed straight from memory as an attachment?
>> Excel can connect directly to SQL Server and create a spreadsheet out of the result of a query.
That's a good idea, however, in this case Excel is being used as a third-party data transfer tool between two databases that cannot connect directly to one another. Therefore the live machines won't have Excel, so .NET is doing the work of reading the data and converting to ADO.NET.
>>> 3) You appear to enumerate row-by-row, as in my example, so surely its not any more efficient?
>> Do you have other suggestions?
Nope, but, being new to this type of thing I wondered if there was any method I wasn't aware of to 'push' the DataTable into a text representation without enumeration being necessary.
I tried getting SQL to export the results as XML, which worked really well, however, Excel 2003 (on the client server) could not open it natively without Importing.
From the links you posted, the first is very similar to what I've done initially, which gives me reassurance that I've not wasted my time, so thanks for that.
The second link involves using a webcontrol, which I do within my web apps (where my skills lie), however, I was researching to see if there was a 'better Forms method' than this. Anyway I'm kind of glad there isn't because at least now I can move on.
Open in new window