Home > Enterprise >  Update excel with data from datatable
Update excel with data from datatable

Time:10-13

I have an Excel file template that my office use to filter their data, i need just update the first sheet "data", i have here my code, i spent more than 3 days to build this routine.
But i find out that is REALLY SLOW, now but i think there is a fastest way to do this.
What can i do?

    private void EsportaInTemplateExcelVL(string NomeFile)
    {        
      
        string fileNameTemp = NomeFile;//System.IO.Path.GetTempPath()   Guid.NewGuid().ToString()   ".xlsx";
        string FileXls = Application.StartupPath   @"\Support\PrintFormat\VL_LAB_updated.xls";

        if (!File.Exists(FileXls))
        {
            MessageBox("File template VL_LAB_updated.xls not found");
            return;
        }

        System.IO.File.Copy(FileXls, fileNameTemp);
        Excel.Application excelApp = new Excel.Application();

        Excel.Workbook wb = excelApp.Application.Workbooks.Open(fileNameTemp, Type.Missing, Type.Missing,
                                                  Type.Missing, Type.Missing,
                                                  Type.Missing, Type.Missing,
                                                  Type.Missing, Type.Missing,
                                                  Type.Missing, Type.Missing,
                                                  Type.Missing, Type.Missing,
                                                  Type.Missing, Type.Missing);

        Excel.Sheets excelSheets = excelApp.Worksheets;// var ws = excelApp.Worksheets;
        Excel.Worksheet excelWorksheet = (Excel.Worksheet)excelSheets.get_Item("DATA");

        string[] col = { "A", "B", "C", "D", "E", "F", "G", "H", "I","J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X","Y", "Z" };



        //Head Columns
        for (int C = 0; C < dt.Columns.Count ; C  )
        {
            string Cella = col[C].ToString()   (0   1).ToString();//i parte da zero

            Excel.Range excelCell = (Excel.Range)excelWorksheet.get_Range(Cella, Cella);
            excelCell.Value = dt.Columns[C].ColumnName;
        }


        //all data
        for (int i = 0; i < dt.Rows.Count ; i  )
        {
            for (int C = 0; C < dt.Columns.Count ; C  )
            {
                string Cella = col[C].ToString()   (i   2).ToString();

                Excel.Range excelCell = (Excel.Range)excelWorksheet.get_Range(Cella, Cella);
                excelCell.Value = "'"   dt.Rows[i][C].ToString();
            }
        }

            wb.Close(true);
            excelApp.Quit();
            MessageBox("Done");
        
    }

CodePudding user response:

You are using ranges but you are defining cells one by one. The good solution is to define cells by range (x rows and x columns) This is much faster in Excel

CodePudding user response:

I propose to use Data Engine to export your data to Excel as it is faster than excel object model. Take a look at following scenario if it fits your needs.

Step 1

Add a new project to your solution. In this demo I named it "MyExtensions".

Step 2

After creating the project, add a new class. In this demo I named it "Extensions".

using System;
using System.Data;
using System.Data.OleDb;
using System.Collections.Generic;
using System.Linq;

namespace MyExtensions
{
    public static class Extensions
    {
    }
}

Step 3

Add following methods to this class.

Method 1

// This method returns Dictionary to convert
// .Net types to Excel types
static Dictionary<Type, string> GetTypeConverter(this OleDbConnection oleDbConnection)
{
    string typeName = "TypeName";
    string dataType = "DataType";
    string columnSize = "ColumnSize";
    Dictionary<Type, string> dictionary = new Dictionary<Type, string>();
    var table = oleDbConnection.GetSchema("DataTypes");
    foreach (DataRow row in table.Select().OrderBy(r => r[columnSize]))
    {
        var type = Type.GetType(row[dataType].ToString());
        if (!dictionary.Keys.Contains(type))
        {
            dictionary.Add(type, row[typeName].ToString());
        }
    }
    return dictionary;
}

Method 2

// This method is helper method. This action will be repeated.
// So it is better to make it an extension method.
public static DataColumn[] ToArray(this DataColumnCollection collection)
{
    var columns = new DataColumn[collection.Count];
    collection.CopyTo(columns, 0);
    return columns;
}

Method 3

// This method adds a new Worksheet to Excel file
private static void CreateWorksheet(this DataTable dataTable, string name,
    OleDbConnection oleDbConnection, Dictionary<Type, string> converter)
{
    var columns = dataTable.Columns.ToArray();
    var query = columns
        .Select(c => $"{c.ColumnName} {converter[c.DataType]}");
    var str1 = string.Join(", ", query);
    var cmdText = $"CREATE TABLE [{name}] ({str1})";
    using (var oleDbCommand = new OleDbCommand(cmdText))
    {
        oleDbCommand.Connection = oleDbConnection;
        oleDbCommand.ExecuteNonQuery();
    }
}

Method 4

// This method exports contents of DataTable.
private static void Export(this DataTable dataTable,
    string worksheetName, OleDbConnection oleDbConnection)
{
    var columns = dataTable.Columns.ToArray();
    var str1 = string.Join(", ", columns.Select(c => $"[{c.ColumnName}]"));
    var str2 = string.Join(", ", columns.Select(c => $"?"));
    var cmdText = $"INSERT INTO [{worksheetName}] ({str1}) VALUES({str2})";
    var parameters = columns.Select(c => new OleDbParameter()).ToArray();

    using (var oleDbCommand = new OleDbCommand(cmdText))
    {
        oleDbCommand.Connection = oleDbConnection;
        oleDbCommand.Parameters.AddRange(parameters);
        var rows = dataTable.Select();
        foreach (var row in rows)
        {
            for (int i = 0; i < columns.Length; i  )
            {
                parameters[i].Value = row[i];
            }
            oleDbCommand.ExecuteNonQuery();
        }
    }
}

Method 5

// This method creates Worksheet in Excel file then 
// exports contents of DataTable.
public static void Export(this DataTable dataTable,
    string excel_FileName, string worksheetName)
{
    var builder = new OleDbConnectionStringBuilder()
    {
        Provider = "Microsoft.ACE.OLEDB.12.0",
        DataSource = excel_FileName
    };
    builder.Add("Extended Properties", "Excel 12.0");

    using (var oleDbConnection = new OleDbConnection(builder.ConnectionString))
    {
        oleDbConnection.Open();
        var converter = oleDbConnection.GetTypeConverter();
        dataTable.CreateWorksheet(worksheetName, oleDbConnection, converter);
        dataTable.Export(worksheetName, oleDbConnection);
    }
}

Step 4

Create a new empty excel file in "My Documents" folder. Name it "Template.xlsx".

Step 5

Now we can use our API in a real situation. I created a console application and added the class library to references node of the project.

I added to my console application a new class to generate a sample DataTable to test to export it to excel.

using System;
using System.Data;

namespace ConsoleApp1
{
    class Factory
    {
        public static DataTable GetSample()
        {
            DataTable dataTable = new DataTable()
            {
                TableName = "DataTable1"
            };
            dataTable.Columns.Add("Id", typeof(int));
            dataTable.Columns.Add("LastName", typeof(string));
            dataTable.Columns.Add("FirstName", typeof(string));
            dataTable.Columns.Add("DateOfBirth", typeof(DateTime));
            dataTable.Rows.Add(1, "John", "Robinson", new DateTime(1980, 6, 12));
            dataTable.Rows.Add(2, "Sara", "Connor", new DateTime(1986, 2, 7));
            return dataTable;
        }
    }
}

Here is Main method of the console application

using System;

namespace ConsoleApp1
{
    using MyExtensions;

    class Program
    {
        static void Main(string[] args)
        {
            var dataTable = Factory.GetSample();
            var path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            var filePath = $"{path}\\Template.xlsx";
            dataTable.Export(filePath, "Data");
        }
    }
}

I had it run and it was successful.

  • Related