How to export DataTable to Excel

后端 未结 21 2346
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-11-22 15:36

How can I export a DataTable to Excel in C#? I am using Windows Forms. The DataTable is associated with a DataGridView control. I have

21条回答
  •  心在旅途
    2020-11-22 15:53

    Private tmr As System.Windows.Forms.Timer
    
    Private Sub TestExcel() Handles Button1.Click
    
        '// Initial data: SQL Server table with 6 columns and 293000 rows.
    
    
        '// Data table holding all data
        Dim dt As New DataTable("F161")
    
        '// Create connection
        Dim conn As New SqlConnection("Server=MYSERVER;Database=Test;Trusted_Connection=Yes;")
        Dim fAdapter As New SqlDataAdapter With
        {
            .SelectCommand = New SqlCommand($"SELECT * FROM dbo.MyTable", conn)
        }
    
        '// Fill DataTable
        fAdapter.Fill(dt)
    
        '// Create Excel application
        Dim xlApp As New Excel.Application With {.Visible = True}
    
        '// Temporarily disable screen updating
        xlApp.ScreenUpdating = False
    
        '// Create brand new workbook
        Dim xlBook As Excel.Workbook = xlApp.Workbooks.Add()
        Dim xlSheet As Excel.Worksheet = DirectCast(xlBook.Sheets(1), Excel.Worksheet)
    
        '// Get number of rows
        Dim rows_count = dt.Rows.Count
        '// Get number of columns
        Dim cols_count = dt.Columns.Count
    
        '// Here 's the core idea: after receiving data
        '// you need to create an array and transfer it to sheet.
        '// Why array?
        '// Because it's the fastest way to transfer data to Excel's sheet.
        '// So, we have two tasks:
        '// 1) Create array
        '// 2) Transfer array to sheet
    
        '// =========================================================
        '// TASK 1: Create array
        '// =========================================================
        '// In order to create array, we need to know that
        '// Excel's Range object expects 2-D array whose lower bounds
        '// of both dimensions start from 1.
        '// This means you can't use C# array.
        '// You need to manually create such array.
        '// Since we already calculated number of rows and columns,
        '// we can use these numbers in creating array.
        Dim arr = Array.CreateInstance(GetType(Object), {rows_count, cols_count}, {1, 1})
    
        '// Fill array
        For r = 0 To rows_count - 1
            For c = 0 To cols_count - 1
                arr(r + 1, c + 1) = dt.Rows(r)(c)
            Next
        Next
    
        '// =========================================================
        '// TASK 2: Transfer array to sheet
        '// =========================================================
        '// Now we need to transfer array to sheet.
        '// So, how transfer array to sheet fast?
        '// 
        '// THE FASTEST WAY TO TRANSFER DATA TO SHEET IS TO ASSIGN ARRAY TO RANGE.
        '// We could, of course, hard-code values, but Resize property
        '// makes this work a breeze:
        xlSheet.Range("A1").Resize.Resize(rows_count, cols_count).Value = arr
    
        '// If we decide to dump data by iterating over array,
        '// it will take LOTS of time.
        '// For r = 1 To rows_count
        '//     For c = 1 To cols_count
        '//         xlSheet.Cells(r, c) = arr(r, c)
        '//     Next
        '// Next
    
        '// Here are time results:
        '// 1) Assigning array to Range: 3 seconds
        '// 2) Iterating over array: 45 minutes
    
        '// Turn updating on
        xlApp.ScreenUpdating = True
        xlApp = Nothing
        xlBook = Nothing
        xlSheet = Nothing
    
        '// Here we have another problem:
        '// creating array took lots of memory (about 150 MB).
        '// Using 'GC.Collect()', by unknown reason, doesn't help here.
        '// However, if you run GC.Collect() AFTER this procedure is finished
        '// (say, by pressing another button and calling another procedure),
        '// then the memory is cleaned up.
        '// I was wondering how to avoid creating some extra button to just release memory,
        '// so I came up with the idea to use timer to call GC.
        '// After 2 seconds GC collects all generations.
        '// Do not forget to dispose timer since we need it only once.
    
        tmr = New Timer()
        AddHandler tmr.Tick,
            Sub()
                GC.Collect()
                GC.WaitForPendingFinalizers()
                GC.WaitForFullGCComplete()
                tmr.Dispose()
            End Sub
        tmr.Interval = TimeSpan.FromSeconds(2).TotalMilliseconds()
        tmr.Start()
    
    End Sub
    

提交回复
热议问题