44

I'm hoping someone here can point me in the right direction - I'm trying to create a fairly robust utility program to read the data from an Excel sheet (may be .xls OR .xlsx) into a DataTable as quickly and leanly as possible.

I came up with this routine in VB (although I'd be just as happy with a good C# answer):

Public Shared Function ReadExcelIntoDataTable(ByVal FileName As String, ByVal SheetName As String) As DataTable
    Dim RetVal As New DataTable

    Dim strConnString As String
    strConnString = "Driver={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)};DBQ=" & FileName & ";"

    Dim strSQL As String 
    strSQL = "SELECT * FROM [" & SheetName & "$]"

    Dim y As New Odbc.OdbcDataAdapter(strSQL, strConnString)

    y.Fill(RetVal)

    Return RetVal

End Function

I'm wondering if this is the best way to do it or if there are better / more efficent ways (or just more intelligent ways - Maybe Linq / native .Net providers) to use instead?

ALSO, just a quick and silly additional question - Do I need to include code such as y.Dispose() and y = Nothing or will that be taken care of since the variable should die at the end of the routine, right??

Thanks!!

John Bustos
  • 19,036
  • 17
  • 89
  • 151
  • 3
    I would use [**EPPlus**](http://epplus.codeplex.com/) and it's `LoadFromDatatable`: http://stackoverflow.com/a/8309265/284240 Note that it supports only `xlsx`. – Tim Schmelter Jan 10 '13 at 15:48
  • Thanks, @Tim - Two questions - 1) Would that work for `.xls`? and 2) Would that prove faster / less resource-hogging than my current routine? – John Bustos Jan 10 '13 at 15:50
  • 1.No 2.Maybe(it's pretty fast) Since i don't know it and it doesn't support older excel versions i've just commented here. – Tim Schmelter Jan 10 '13 at 15:53
  • 2
    @TimSchmelter how does `LoadFromDatatable` of `EPPlus` load Excel file to data table? As far as I know, this method support writing the data table into Excel file – macemers Apr 08 '14 at 10:22
  • @user838204: good catch, i think that i've misunderstood the requirement at that time. If you're looking for a way you might want to have a look at [this approach](http://stackoverflow.com/a/13396787/284240) i've posted sometime. – Tim Schmelter Apr 08 '14 at 10:28
  • @TimSchmelter Thanks for the update. BTW, I think this library (https://exceldatareader.codeplex.com/) is an easy way to read `Excel` to data table – macemers Apr 09 '14 at 01:24
  • In our company, we've had numerous problems (deployment & security) using OLEDB, and now, we mainly use OpenXML to read in Excel files. I've posted the OpenXML code which I use here: http://stackoverflow.com/a/43799771/391605 – Mike Gledhill May 05 '17 at 08:13

12 Answers12

51

If you want to do the same thing in C# based on Ciarán Answer

string sSheetName = null;
string sConnection = null;
DataTable dtTablesList = default(DataTable);
OleDbCommand oleExcelCommand = default(OleDbCommand);
OleDbDataReader oleExcelReader = default(OleDbDataReader);
OleDbConnection oleExcelConnection = default(OleDbConnection);

sConnection = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\\Test.xls;Extended Properties=\"Excel 12.0;HDR=No;IMEX=1\"";

oleExcelConnection = new OleDbConnection(sConnection);
oleExcelConnection.Open();

dtTablesList = oleExcelConnection.GetSchema("Tables");

if (dtTablesList.Rows.Count > 0) 
{
    sSheetName = dtTablesList.Rows[0]["TABLE_NAME"].ToString();
}

dtTablesList.Clear();
dtTablesList.Dispose();


if (!string.IsNullOrEmpty(sSheetName)) {
    oleExcelCommand = oleExcelConnection.CreateCommand();
    oleExcelCommand.CommandText = "Select * From [" + sSheetName + "]";
    oleExcelCommand.CommandType = CommandType.Text;
    oleExcelReader = oleExcelCommand.ExecuteReader();
    nOutputRow = 0;

    while (oleExcelReader.Read())
    {
    }
    oleExcelReader.Close();
}
oleExcelConnection.Close();

here is another way read Excel into a DataTable without using OLEDB very quick Keep in mind that the file ext would have to be .CSV for this to work properly

private static DataTable GetDataTabletFromCSVFile(string csv_file_path)
{
    csvData = new DataTable(defaultTableName);
    try
    {
        using (TextFieldParser csvReader = new TextFieldParser(csv_file_path))
        {
            csvReader.SetDelimiters(new string[]
            {
                tableDelim 
            });
            csvReader.HasFieldsEnclosedInQuotes = true;
            string[] colFields = csvReader.ReadFields();
            foreach (string column in colFields)
            {
                DataColumn datecolumn = new DataColumn(column);
                datecolumn.AllowDBNull = true;
                csvData.Columns.Add(datecolumn);
            }

            while (!csvReader.EndOfData)
            {
                string[] fieldData = csvReader.ReadFields();
                //Making empty value as null
                for (int i = 0; i < fieldData.Length; i++)
                {
                    if (fieldData[i] == string.Empty)
                    {
                        fieldData[i] = string.Empty; //fieldData[i] = null
                    }
                    //Skip rows that have any csv header information or blank rows in them
                    if (fieldData[0].Contains("Disclaimer") || string.IsNullOrEmpty(fieldData[0]))
                    {
                        continue;
                    }
                }
                csvData.Rows.Add(fieldData);
            }
        }
    }
    catch (Exception ex)
    {
    }
    return csvData;
}
Keith
  • 1,331
  • 2
  • 13
  • 18
MethodMan
  • 18,625
  • 6
  • 34
  • 52
  • do you mean `tableDelim` – MethodMan Sep 09 '14 at 15:48
  • it's a static up top declared as `static string tableDelim = ",";` – MethodMan Sep 09 '14 at 17:46
  • Some of the conversion above in the first code snippet will need to be changed. Such as `sSheetName = dtTablesList.Rows(0)("TABLE_NAME").ToString;` to `sSheetName = dtTablesList.Rows[0]["TABLE_NAME"].ToString();`. Also `oleExcelCommand.ExecuteReader` to `oleExcelCommand.ExecuteReader()` and `oleExcelReader.Read` to `oleExcelReader.Read()`. – famousKaneis Dec 09 '14 at 17:05
  • thanks CriticalException feel free to edit if you like.. I am in the middle of a critical release.. thanks – MethodMan Dec 09 '14 at 17:10
36

I have always used OLEDB for this, something like...

    Dim sSheetName As String
    Dim sConnection As String
    Dim dtTablesList As DataTable
    Dim oleExcelCommand As OleDbCommand
    Dim oleExcelReader As OleDbDataReader
    Dim oleExcelConnection As OleDbConnection

    sConnection = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Test.xls;Extended Properties=""Excel 12.0;HDR=No;IMEX=1"""

    oleExcelConnection = New OleDbConnection(sConnection)
    oleExcelConnection.Open()

    dtTablesList = oleExcelConnection.GetSchema("Tables")

    If dtTablesList.Rows.Count > 0 Then
        sSheetName = dtTablesList.Rows(0)("TABLE_NAME").ToString
    End If

    dtTablesList.Clear()
    dtTablesList.Dispose()

    If sSheetName <> "" Then

        oleExcelCommand = oleExcelConnection.CreateCommand()
        oleExcelCommand.CommandText = "Select * From [" & sSheetName & "]"
        oleExcelCommand.CommandType = CommandType.Text

        oleExcelReader = oleExcelCommand.ExecuteReader

        nOutputRow = 0

        While oleExcelReader.Read

        End While

        oleExcelReader.Close()

    End If

    oleExcelConnection.Close()

The ACE.OLEDB provider will read both .xls and .xlsx files and I have always found the speed quite good.

SyedAliNaqi
  • 244
  • 1
  • 12
Ciarán
  • 3,017
  • 1
  • 16
  • 20
  • Awesome!! Thanks, @Ciaran - I changed my code up to use the OleDB provider and all looks like it's working perfectly!! Thanks!! – John Bustos Jan 10 '13 at 19:39
  • 1
    This code is working in my local machine,but when I published it in server -I'm getting the error "'Microsoft.ACE.OLEDB.12.0' provider is not registered on the local machine.".I installed same from "https://www.microsoft.com/en-us/download/details.aspx?id=13255".But still no go. – Sribin Apr 02 '18 at 07:16
  • Version Hell I suspect, try the [2007 redistributable instead](https://www.microsoft.com/en-gb/download/details.aspx?id=23734). – Ciarán Apr 04 '18 at 08:25
  • I use it extensively with SSIS on a SQL/Server cluster. It will run on a server as long as you've got the driver installed. – Ciarán Dec 19 '20 at 18:19
10
public DataTable ImportExceltoDatatable(string filepath)
{
    // string sqlquery= "Select * From [SheetName$] Where YourCondition";
    string sqlquery = "Select * From [SheetName$] Where Id='ID_007'";
    DataSet ds = new DataSet();
    string constring = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + filepath + ";Extended Properties=\"Excel 12.0;HDR=YES;\"";
    OleDbConnection con = new OleDbConnection(constring + "");
    OleDbDataAdapter da = new OleDbDataAdapter(sqlquery, con);
    da.Fill(ds);
    DataTable dt = ds.Tables[0];
    return dt;
}
Nikolay Kostov
  • 16,433
  • 23
  • 85
  • 123
Debasish
  • 369
  • 4
  • 12
9

This seemed to work pretty well for me.

private DataTable ReadExcelFile(string sheetName, string path)
{

    using (OleDbConnection conn = new OleDbConnection())
    {
        DataTable dt = new DataTable();
        string Import_FileName = path;
        string fileExtension = Path.GetExtension(Import_FileName);
        if (fileExtension == ".xls")
            conn.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + Import_FileName + ";" + "Extended Properties='Excel 8.0;HDR=YES;'";
        if (fileExtension == ".xlsx")
            conn.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + Import_FileName + ";" + "Extended Properties='Excel 12.0 Xml;HDR=YES;'";
        using (OleDbCommand comm = new OleDbCommand())
        {
            comm.CommandText = "Select * from [" + sheetName + "$]";
            comm.Connection = conn;
            using (OleDbDataAdapter da = new OleDbDataAdapter())
            {
                da.SelectCommand = comm;
                da.Fill(dt);
                return dt;
            }
        }
    }
}
Chandan Kumar
  • 4,570
  • 4
  • 42
  • 62
Hexxx
  • 413
  • 5
  • 13
5

You can use OpenXml SDK for *.xlsx files. It works very quickly. I made simple C# IDataReader implementation for this sdk. See here. Now you can easy read excel file to DataTable and you can import excel file to sql server database (use SqlBulkCopy). ExcelDataReader reads very fast. On my machine 10000 records less 3 sec and 60000 less 8 sec.

Read to DataTable example:

class Program
{
    static void Main(string[] args)
    {
        var dt = new DataTable();
        using (var reader = new ExcelDataReader(@"data.xlsx"))
            dt.Load(reader);

        Console.WriteLine("done: " + dt.Rows.Count);
        Console.ReadKey();
   }
}
Sergey Petrov
  • 121
  • 1
  • 4
  • It's strange... When I called your ExcelDataReader() function with just a filename, it correctly read in the first Worksheet's data. But when I provided a Worksheet name, I just got an empty DataTable as a result (no errors, no exceptions, and the DataTable.Load() did run okay). Strange... – Mike Gledhill May 05 '17 at 07:04
  • Just to add, my issue only occurred on worksheets with 1 or more blank rows at the top. No errors occurred in the code, everything ran fine, but DataTable.Load() simply read in a DataTable of 0 rows and 0 columns. I could read in all other worksheets fine using ExcelDataReader. – Mike Gledhill May 08 '17 at 09:42
  • Thank you for your comment. I fixed this problem. – Sergey Petrov May 08 '17 at 12:27
4

I found it pretty easy like this

    using System;
    using System.Data;
    using System.IO;
    using Excel;

    public DataTable ExcelToDataTableUsingExcelDataReader(string storePath)
    {
        FileStream stream = File.Open(storePath, FileMode.Open, FileAccess.Read);

        string fileExtension = Path.GetExtension(storePath);
        IExcelDataReader excelReader = null;
        if (fileExtension == ".xls")
        {
            excelReader = ExcelReaderFactory.CreateBinaryReader(stream);
        }
        else if (fileExtension == ".xlsx")
        {
            excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream);
        }

        excelReader.IsFirstRowAsColumnNames = true;
        DataSet result = excelReader.AsDataSet();
        var test = result.Tables[0];
        return result.Tables[0];
    }

Note: you need to install SharpZipLib package for this

Install-Package SharpZipLib

neat and clean! ;)

  • 2
    Among all solutions proposed here, yours is for me the best. It doesn't require excel oledb which require to install a third party application on every PC using the import function. To use your solution I didn't needed to install SharpZipLib, but ExcelDataReader and ExcelDataReader.DataSet. Too, this line "excelReader.IsFirstRowAsColumnNames = true;" is not working anymore. – Raphael Jun 15 '18 at 13:52
  • however this one is very slow. – yww325 May 06 '20 at 21:39
3

This is the way to read from excel oledb

try
{
    System.Data.OleDb.OleDbConnection MyConnection;
    System.Data.DataSet DtSet;
    System.Data.OleDb.OleDbDataAdapter MyCommand;
    string strHeader7 = "";
    strHeader7 = (hdr7) ? "Yes" : "No";
    MyConnection = new System.Data.OleDb.OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + fn + ";Extended Properties=\"Excel 12.0;HDR=" + strHeader7 + ";IMEX=1\"");
    MyCommand = new System.Data.OleDb.OleDbDataAdapter("select * from [" + wks + "$]", MyConnection);
    MyCommand.TableMappings.Add("Table", "TestTable");
    DtSet = new System.Data.DataSet();
    MyCommand.Fill(DtSet);
    dgv7.DataSource = DtSet.Tables[0];
    MyConnection.Close();
}
catch (Exception ex)
{
    MessageBox.Show(ex.ToString());
}
Mateusz
  • 3,038
  • 4
  • 27
  • 41
rony sc
  • 71
  • 2
  • ```hdr7``` would be a Boolean value passed by the consumer of this method. ```fn``` would be the excel file.```wks``` would be the sheet name of the excel file (which supposed to be found prior to using it) Bottom line: job half done :( – Gabriel Marius Popescu Jun 28 '18 at 10:22
2
''' <summary>
''' ReadToDataTable reads the given Excel file to a datatable.
''' </summary>
''' <param name="table">The table to be populated.</param>
''' <param name="incomingFileName">The file to attempt to read to.</param>
''' <returns>TRUE if success, FALSE otherwise.</returns>
''' <remarks></remarks>
Public Function ReadToDataTable(ByRef table As DataTable,
                                incomingFileName As String) As Boolean
    Dim returnValue As Boolean = False
    Try

        Dim sheetName As String = ""
        Dim connectionString As String = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & incomingFileName & ";Extended Properties=""Excel 12.0;HDR=No;IMEX=1"""
        Dim tablesInFile As DataTable
        Dim oleExcelCommand As OleDbCommand
        Dim oleExcelReader As OleDbDataReader
        Dim oleExcelConnection As OleDbConnection

        oleExcelConnection = New OleDbConnection(connectionString)
        oleExcelConnection.Open()

        tablesInFile = oleExcelConnection.GetSchema("Tables")

        If tablesInFile.Rows.Count > 0 Then
            sheetName = tablesInFile.Rows(0)("TABLE_NAME").ToString
        End If

        If sheetName <> "" Then

            oleExcelCommand = oleExcelConnection.CreateCommand()
            oleExcelCommand.CommandText = "Select * From [" & sheetName & "]"
            oleExcelCommand.CommandType = CommandType.Text

            oleExcelReader = oleExcelCommand.ExecuteReader

            'Determine what row of the Excel file we are on
            Dim currentRowIndex As Integer = 0

            While oleExcelReader.Read
                'If we are on the First Row, then add the item as Columns in the DataTable
                If currentRowIndex = 0 Then
                    For currentFieldIndex As Integer = 0 To (oleExcelReader.VisibleFieldCount - 1)
                        Dim currentColumnName As String = oleExcelReader.Item(currentFieldIndex).ToString
                        table.Columns.Add(currentColumnName, GetType(String))
                        table.AcceptChanges()
                    Next
                End If
                'If we are on a Row with Data, add the data to the SheetTable
                If currentRowIndex > 0 Then
                    Dim newRow As DataRow = table.NewRow
                    For currentFieldIndex As Integer = 0 To (oleExcelReader.VisibleFieldCount - 1)
                        Dim currentColumnName As String = table.Columns(currentFieldIndex).ColumnName
                        newRow(currentColumnName) = oleExcelReader.Item(currentFieldIndex)
                        If IsDBNull(newRow(currentFieldIndex)) Then
                            newRow(currentFieldIndex) = ""
                        End If
                    Next
                    table.Rows.Add(newRow)
                    table.AcceptChanges()
                End If

                'Increment the CurrentRowIndex
                currentRowIndex += 1
            End While

            oleExcelReader.Close()

        End If

        oleExcelConnection.Close()
        returnValue = True
    Catch ex As Exception
        'LastError = ex.ToString
        Return False
    End Try


    Return returnValue
End Function
Vince Pike
  • 620
  • 6
  • 13
2

The below code is tested by myself and is very simple, understandable, usable and fast. This code, initially takes all sheet names, then puts all tables of that excel file in a DataSet.

    public static DataSet ToDataSet(string exceladdress, int startRecord = 0, int maxRecord = -1, string condition = "")
    {
        DataSet result = new DataSet();
        using (OleDbConnection connection = new OleDbConnection(
                (exceladdress.TrimEnd().ToLower().EndsWith("x"))
                ? "Provider=Microsoft.ACE.OLEDB.12.0;Data Source='" + exceladdress + "';" + "Extended Properties='Excel 12.0 Xml;HDR=YES;'"
                : "provider=Microsoft.Jet.OLEDB.4.0;Data Source='" + exceladdress + "';Extended Properties=Excel 8.0;"))
            try
            {
                connection.Open();
                DataTable schema = connection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
                foreach (DataRow drSheet in schema.Rows)
                    if (drSheet["TABLE_NAME"].ToString().Contains("$"))
                    {
                        string s = drSheet["TABLE_NAME"].ToString();
                        if (s.StartsWith("'")) s = s.Substring(1, s.Length - 2);
                        System.Data.OleDb.OleDbDataAdapter command =
                            new System.Data.OleDb.OleDbDataAdapter(string.Join("", "SELECT * FROM [", s, "] ", condition), connection);
                        DataTable dt = new DataTable();
                        if (maxRecord > -1 && startRecord > -1) command.Fill(startRecord, maxRecord, dt);
                        else command.Fill(dt);
                        result.Tables.Add(dt);
                    }
                return result;
            }
            catch (Exception ex) { return null; }
            finally { connection.Close(); }
    }

Enjoy...

MiMFa
  • 981
  • 11
  • 14
0

Use the below snippet it will be helpfull.

string POCpath = @"G:\Althaf\abc.xlsx";

string POCConnection = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + POCpath + ";Extended Properties=\"Excel 12.0;HDR=Yes;IMEX=1\";";

OleDbConnection POCcon = new OleDbConnection(POCConnection);
OleDbCommand POCcommand = new OleDbCommand();
DataTable dt = new DataTable();
OleDbDataAdapter POCCommand = new OleDbDataAdapter("select * from [Sheet1$] ", POCcon);
POCCommand.Fill(dt);
Console.WriteLine(dt.Rows.Count);
Dovydas Šopa
  • 2,282
  • 8
  • 26
  • 34
althaf a s
  • 71
  • 6
-1

I've used this method and for me, it is so efficient and fast.

// Step 1. Download NuGet source of Generic Parsing by Andrew Rissing
// Step 2. Reference this to your project
// Step 3. Reference Microsoft.Office.Interop.Excel to your project
// Step 4. Follow the logic below

public static DataTable ExcelSheetToDataTable(string filePath) {

    // Save a copy of the Excel file as CSV
    var xlApp = new XL.Application();
    var xlWbk = xlApp.Workbooks.Open(filePath);
    var tempPath =
        Path.Combine(Environment
            .GetFolderPath(Environment.SpecialFolder.UserProfile)
            , "AppData"
            , "Local",
            , "Temp"
            , Path.GetFileNameWithoutExtension(filePath) + ".csv");

    xlApp.DisplayAlerts = false;
    xlWbk.SaveAs(tempPath, XL.XlFileFormat.xlCSV);
    xlWbk.Close(SaveChanges: false);
    xlApp.Quit();

    // The actual parsing
    using (var parser = new GenericParserAdapter(tempPath)) {
        parser.FirstRowHasHeader = true;
        return parser.GetDataTable();
    }

}

Generic Parsing by Andrew Rissing

morethanyell
  • 316
  • 3
  • 10
-1

Here is another way of doing it

public DataSet CreateTable(string source)
{
    using (var connection = new OleDbConnection(GetConnectionString(source, true)))
    {
        var dataSet = new DataSet();
        connection.Open();
        var schemaTable = connection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
        if (schemaTable == null)
            return dataSet;

        var sheetName = "";
        foreach (DataRow row in schemaTable.Rows)
        {
            sheetName = row["TABLE_NAME"].ToString();
            break;
        }

        var command = string.Format("SELECT * FROM [{0}$]", sheetName);
        var adapter = new OleDbDataAdapter(command, connection);
        adapter.TableMappings.Add("TABLE", "TestTable");
        adapter.Fill(dataSet);
        connection.Close();

        return dataSet;
    }
}

//

private string GetConnectionString(string source, bool hasHeader)
{
    return string.Format("Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};
    Extended Properties=\"Excel 12.0;HDR={1};IMEX=1\"", source, (hasHeader ? "YES" : "NO"));
}
Gabriel Marius Popescu
  • 2,016
  • 2
  • 20
  • 22