Ok, this is an interesting issue. I have been tasked with modifying an existing VB project. Currently the user selected from a series of dropdowns to select a sql query and then run that query. So the user selects and environment dropdown, the results of that dropdown populates the category dropdown. Once the category is selected, they get a dropdown of available queries. Once they select a query and hit the "Run" button, they get a gridview with the results of the query. Some of the query results are huge. The query I'm running as a test has 40 columns and 20,000 records. The query runs in less than 5 seconds but it takes over a minute to render the gridview. Once the gridview is done rendering, the user has the option to export the results to Excel. And by this, I mean the code opens an instance of Excel through gridview.RenderControl and displays the results in Excel. The user doesn't want to save the excel file and then navigate to the file, they want it to open right from the webform they are using which is what the code does currently.
However, the user doesn't care about the gridview. They don't care if they see it at all. They want to just open Excel. So instead of using gridview.RenderControl, I want to open Excel and populate it with the DataTable (or DataSet) in memory. Any thoughts on the best way to do that?
Here's how they are currently populating the gridview: Dim MyConnection As SqlConnection Dim MyCommand As SqlCommand Dim MyDataTable As DataTable Dim MyReader As SqlDataReader
MyConnection = New SqlConnection()
MyConnection.ConnectionString = ConfigurationManager.ConnectionStrings(Connection).ConnectionString
MyCommand = New SqlCommand()
MyCommand.CommandText = Sqlquery
MyCommand.CommandType = CommandType.Text
MyCommand.Connection = MyConnection
MyCommand.Connection.Open()
MyReader = MyCommand.ExecuteReader(CommandBehavior.CloseConnection)
MyDataTable = New DataTable()
MyDataTable.Load(MyReader)
If (MyDataTable.Rows.Count > 0) Then
QueryresultPanel.Visible = True
gvLineItems.DataSource = MyDataTable
gvLineItems.DataBind()
End If
MyDataTable.Dispose()
MyCommand.Dispose()
MyConnection.Dispose()
Here's how they're opening and populating the instance of Excel:
Protected Sub btnExportToExcel_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnExportToExcel.Click
Response.Clear()
Response.Buffer = True
'
' Set the content type to Excel.
'
Response.AddHeader("content-disposition", "attachment;filename=GridViewExport.xls")
Response.Charset = ""
Response.ContentType = "application/vnd.ms-excel"
'
' Turn off the view state.
'
Me.EnableViewState = False
Dim oStringWriter As New System.IO.StringWriter()
Dim oHtmlTextWriter As New System.Web.UI.HtmlTextWriter(oStringWriter)
'
' Get the HTML for the control.
'
gvLineItems.RenderControl(oHtmlTextWriter)
'
' Write the HTML back to the browser.
'
Response.Write(oStringWriter.ToString())
Response.[End]()
End Sub
Obviously, there's no RenderControl for a DataTable or DataSet and can't figure out how to get this record set to render in an instance of Excel without saving it to a file first.
Alright, here's the solution I found (in case anyone is interested). It's pretty simple actually. I just looped through the datatable and used StringWriter.
Protected Sub WriteToExcelFile(dt As DataTable)
Dim sw As StringWriter
For Each datacol As DataColumn In dt.Columns
sw.Write(datacol.ColumnName + vbTab)
Next
Dim row As DataRow
For Each row In dt.Rows
sw.Write(vbNewLine)
Dim column As DataColumn
For Each column In dt.Columns
If Not row(column.ColumnName) Is Nothing Then
sw.Write(row(column).ToString() + vbTab)
Else
sw.Write(String.Empty + vbTab)
End If
Next column
Next row
Response.Clear()
Response.ContentType = "application/vnd.ms-excel"
Response.AddHeader("Content-Disposition", "attachment;filename=DataTable.xls")
Response.Output.Write(sw.ToString())
Response.Flush()
System.Web.HttpContext.Current.Response.Flush()
System.Web.HttpContext.Current.Response.SuppressContent = True
System.Web.HttpContext.Current.ApplicationInstance.CompleteRequest()
End Sub