Excel to DataTable using EPPlus - excel locked for editing
A extension version of Tim Schmelter's answer.
public static DataTable ToDataTable(this ExcelWorksheet ws, bool hasHeaderRow = true)
{
var tbl = new DataTable();
foreach (var firstRowCell in ws.Cells[1, 1, 1, ws.Dimension.End.Column])
tbl.Columns.Add(hasHeaderRow ?
firstRowCell.Text : string.Format("Column {0}", firstRowCell.Start.Column));
var startRow = hasHeaderRow ? 2 : 1;
for (var rowNum = startRow; rowNum <= ws.Dimension.End.Row; rowNum++)
{
var wsRow = ws.Cells[rowNum, 1, rowNum, ws.Dimension.End.Column];
var row = tbl.NewRow();
foreach (var cell in wsRow) row[cell.Start.Column - 1] = cell.Text;
tbl.Rows.Add(row);
}
return tbl;
}
I see, that's what i've posted recently here(now corrected). It can be improved since the ExcelPackage
and the FileStream
(from File.OpenRead
) are not disposed after using.
public static DataTable GetDataTableFromExcel(string path, bool hasHeader = true)
{
using (var pck = new OfficeOpenXml.ExcelPackage())
{
using (var stream = File.OpenRead(path))
{
pck.Load(stream);
}
var ws = pck.Workbook.Worksheets.First();
DataTable tbl = new DataTable();
foreach (var firstRowCell in ws.Cells[1, 1, 1, ws.Dimension.End.Column])
{
tbl.Columns.Add(hasHeader ? firstRowCell.Text : string.Format("Column {0}", firstRowCell.Start.Column));
}
var startRow = hasHeader ? 2 : 1;
for (int rowNum = startRow; rowNum <= ws.Dimension.End.Row; rowNum++)
{
var wsRow = ws.Cells[rowNum, 1, rowNum, ws.Dimension.End.Column];
DataRow row = tbl.Rows.Add();
foreach (var cell in wsRow)
{
row[cell.Start.Column - 1] = cell.Text;
}
}
return tbl;
}
}
I've created a method that converts an Excel file to a DataTable using EPPlus, and tried to maintain Type Safety. Also duplicate column names are handled and with a boolean you can tell the method wether the sheet has a row with headers. I've created it for a complex import process that has several steps after uploading that requires user input before committing to the database.
private DataTable ExcelToDataTable(byte[] excelDocumentAsBytes, bool hasHeaderRow)
{
DataTable dt = new DataTable();
string errorMessages = "";
//create a new Excel package in a memorystream
using (MemoryStream stream = new MemoryStream(excelDocumentAsBytes))
using (ExcelPackage excelPackage = new ExcelPackage(stream))
{
ExcelWorksheet worksheet = excelPackage.Workbook.Worksheets[1];
//check if the worksheet is completely empty
if (worksheet.Dimension == null)
{
return dt;
}
//add the columns to the datatable
for (int j = worksheet.Dimension.Start.Column; j <= worksheet.Dimension.End.Column; j++)
{
string columnName = "Column " + j;
var excelCell = worksheet.Cells[1, j].Value;
if (excelCell != null)
{
var excelCellDataType = excelCell;
//if there is a headerrow, set the next cell for the datatype and set the column name
if (hasHeaderRow == true)
{
excelCellDataType = worksheet.Cells[2, j].Value;
columnName = excelCell.ToString();
//check if the column name already exists in the datatable, if so make a unique name
if (dt.Columns.Contains(columnName) == true)
{
columnName = columnName + "_" + j;
}
}
//try to determine the datatype for the column (by looking at the next column if there is a header row)
if (excelCellDataType is DateTime)
{
dt.Columns.Add(columnName, typeof(DateTime));
}
else if (excelCellDataType is Boolean)
{
dt.Columns.Add(columnName, typeof(Boolean));
}
else if (excelCellDataType is Double)
{
//determine if the value is a decimal or int by looking for a decimal separator
//not the cleanest of solutions but it works since excel always gives a double
if (excelCellDataType.ToString().Contains(".") || excelCellDataType.ToString().Contains(","))
{
dt.Columns.Add(columnName, typeof(Decimal));
}
else
{
dt.Columns.Add(columnName, typeof(Int64));
}
}
else
{
dt.Columns.Add(columnName, typeof(String));
}
}
else
{
dt.Columns.Add(columnName, typeof(String));
}
}
//start adding data the datatable here by looping all rows and columns
for (int i = worksheet.Dimension.Start.Row + Convert.ToInt32(hasHeaderRow); i <= worksheet.Dimension.End.Row; i++)
{
//create a new datatable row
DataRow row = dt.NewRow();
//loop all columns
for (int j = worksheet.Dimension.Start.Column; j <= worksheet.Dimension.End.Column; j++)
{
var excelCell = worksheet.Cells[i, j].Value;
//add cell value to the datatable
if (excelCell != null)
{
try
{
row[j - 1] = excelCell;
}
catch
{
errorMessages += "Row " + (i - 1) + ", Column " + j + ". Invalid " + dt.Columns[j - 1].DataType.ToString().Replace("System.", "") + " value: " + excelCell.ToString() + "<br>";
}
}
}
//add the new row to the datatable
dt.Rows.Add(row);
}
}
//show error messages if needed
Label1.Text = errorMessages;
return dt;
}
The webforms button click for demo purposes.
protected void Button1_Click(object sender, EventArgs e)
{
if (FileUpload1.HasFile)
{
DataTable dt = ExcelToDataTable(FileUpload1.FileBytes, CheckBox1.Checked);
GridView1.DataSource = dt;
GridView1.DataBind();
}
}