Getting Excel data to SQL table with EPPlus and SqlBulkCopy - c#

I already have added the EPPlus library to my solution. I just can't seem to figure out how to get my excel data it into a datatable that will allow my bulkcopy to work. The below code doesn't work. Can anyone help me massage this into place? Thank you in advance for your assistance. I have edited this after comments from 'mason' below.
try
{
//// open file
var excel = Request.Files[0];
var file = Path.Combine(Server.MapPath("~/Uploads/"), excel.FileName);
var sqlConnectionString = ConfigurationManager.ConnectionStrings["MyDB"].ToString();
// Get the datatable from procedure on Utility.cs page
var datapush = Utility.ImportToDataTable(file, "Sheet1");
// open connection to sql and use bulk copy to write excelData to my table
using (var destinationConnection = new SqlConnection(sqlConnectionString))
{
destinationConnection.Open();
using (var bulkCopy = new SqlBulkCopy(destinationConnection))
{
bulkCopy.DestinationTableName = "MYTABLE";
bulkCopy.ColumnMappings.Add("CODE", "code");
bulkCopy.ColumnMappings.Add("TITLE", "title");
bulkCopy.ColumnMappings.Add("LAST_NAME", "last_name");
bulkCopy.ColumnMappings.Add("FIRST_NAME", "first_name");
bulkCopy.WriteToServer(datapush);
}
}
}
and here is the code on the Utility.cs page based on Mason's suggested link:
public class Utility
{
public static DataTable ImportToDataTable(string FilePath, string SheetName)
{
DataTable dt = new DataTable();
FileInfo fi = new FileInfo(FilePath);
// Check if the file exists
if (!fi.Exists)
throw new Exception("File " + FilePath + " Does Not Exists");
using (ExcelPackage xlPackage = new ExcelPackage(fi))
{
// get the first worksheet in the workbook
ExcelWorksheet worksheet = xlPackage.Workbook.Worksheets[SheetName];
// Fetch the WorkSheet size
ExcelCellAddress startCell = worksheet.Dimension.Start;
ExcelCellAddress endCell = worksheet.Dimension.End;
// create all the needed DataColumn
for (int col = startCell.Column; col <= endCell.Column; col++)
dt.Columns.Add(col.ToString());
// place all the data into DataTable
for (int row = startCell.Row; row <= endCell.Row; row++)
{
DataRow dr = dt.NewRow();
int x = 0;
for (int col = startCell.Column; col <= endCell.Column; col++)
{
dr[x++] = worksheet.Cells[row, col].Value;
}
dt.Rows.Add(dr);
}
}
return dt;
}
}
Currently when I run the code and F11 the bug is on the Utility.cs page. right after "// get the first worksheet in the workbook"
ExcelWorksheet worksheet = xlPackage.Workbook.Worksheets[SheetName];
returns null and the next line of code
ExcelCellAddress startCell = worksheet.Dimension.Start;
stops everything and kicks the following error "{"Object reference not set to an instance of an object."}"

Related

How to adjust Excel columns count to project into DataTable in C#?

I am trying to fetch data from Excel sheet and fill data into an DataTable in c# by using EPPlus by this code:
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
using(var _excel = new ExcelPackage(new FileInfo(fileAddress)){
var data = excel.Workbook.Worksheets[0].Cells.ToDataTable(options =>
{
options.FirstRowIsColumnNames = true;
options.EmptyRowStrategy = EmptyRowsStrategy.StopAtFirst;
});
}
but when I am running this code I am getting this error:
first row contains an empty cell at index 4
which 4 is the index of the first empty cell after data ends.
I try to set column mapping but no progress has been made. is there any way to determine the table dimension to cast to DataTable?
Add Nuget package of ClosedXML to the solution.
using ClosedXML.Excel;
call this function when you want in your code:
string strFileName = #"C:\VSTSInput\InputFileVSTS_File1.xlsx";
string strSheetName = "Sheet 1";
DataTable DT_InputData = GetDataFromExcel(strFileName, strSheetName);
Create the following as a method, to reuse it:
public static DataTable GetDataFromExcel(string path, dynamic worksheet)
{
DataTable dt = new DataTable();
//Open the Excel file using ClosedXML.
using (XLWorkbook workBook = new XLWorkbook(path))
{
//Read the first Sheet from Excel file.
IXLWorksheet workSheet = workBook.Worksheet(worksheet);
//Create a new DataTable.
//Loop through the Worksheet rows.
bool firstRow = true;
foreach (IXLRow row in workSheet.Rows())
{
//Use the first row to add columns to DataTable.
if (firstRow)
{
foreach (IXLCell cell in row.Cells())
{
if (!string.IsNullOrEmpty(cell.Value.ToString()))
{
dt.Columns.Add(cell.Value.ToString());
}
else
{
break;
}
}
firstRow = false;
}
else
{
int i = 0;
DataRow toInsert = dt.NewRow();
foreach (IXLCell cell in row.Cells(1, dt.Columns.Count))
{
try
{
toInsert[i] = cell.Value.ToString();
}
catch (Exception ex)
{
Console.WriteLine("Failed at: " + System.Reflection.MethodBase.GetCurrentMethod().Name);
Console.WriteLine(ex.Message);
}
i++;
}
dt.Rows.Add(toInsert);
}
}
return dt;
}
}
Your data from the excel will be added to the DataTable and you can access the columns by the following code:
foreach (DataRow dtRow in DT_InputData.Rows)
{
var dataColumn1 = dtRow[0].ToString(); //Data of 1st column in excel
var dataColumn2 = dtRow[1].ToString(); //Data of 2nd column in excel
var dataColumn3 = dtRow["Your Excel Column Name"].ToString();
}

Some data is missing in the Export to Excel using DataTable and Linq

I am exporting three worked sheet in single XL file, but I am missing some user data in the second DataTable (Education Details sheet) and third DataTable (Employeement Details sheet).
The Education Details sheet is some users are not there, but an Employeement Details sheet that users are showing. User Email Id's is there all three Database Tables.
DataSe ds = new DataSet();
DataTable dt = new DataTable("Registration Details");
DataTable dt1 = new DataTable("Education Details");
DataTable dt2 = new DataTable("Employeement Details");
dt = bl.Get_Registrationdetailsbydate(bo);
gv_Regdetails.DataSource = dt;
gv_Regdetails.DataBind();
dt1 = bl.Get_Registrationdetailsbydate1(bo);
dt2 = bl.Get_Registrationdetailsbydate2(bo);
DataTable filteredEducation = dt1.AsEnumerable()
.Where(x => dt.AsEnumerable()
.Any(z => z.Field<string>("Email").Trim() == x.Field<string>("Email").Trim()))
.CopyToDataTable();
DataTable filteredEmployee = dt2.AsEnumerable()
.Where(x => dt.AsEnumerable()
.Any(z => z.Field<string>("Email").Trim() == x.Field<string>("Email").Trim()))
.CopyToDataTable();
dt.TableName = "Registration Details";
filteredEducation.TableName = "Education Details";
filteredEmployee.TableName = "Employeement Details";
ds.Tables.Add(dt);
ds.Tables.Add(filteredEducation);
ds.Tables.Add(filteredEmployee);
ExcelHelper.ToExcel(ds, "DangoteUsers.xls", Page.Response);
I did result base on first DataTable users Email, then fill second DataTable detail users base on first DataTable Email id's. Same as Employment Details. The issue in first DataTable and second DataTable. I am not returning the DataTable also.
I refer this example
The problem is coming somewhere from the solution of conversion from DataSet to Excel in the article. Using this self made conversion is not a good idea. Use Jet/ACE engine or Microsoft Office Interop. At least they guarantee, they don't have such kind of bugs, which in future can became more. Better use something which is already highly accepted by the community. Here I wrote an approach how to do it with Interop.
First what you need to do is to add the reference to Microsoft.Office.Interop.Excel. Here is how to do it, taken from msdn article
Add the Excel assembly as a reference to the project: Right-click on
the project, select Add Reference.
Click the COM tab of the Add Reference dialog box, and find Microsoft
Excel 11 Object Library.
Double-click on Microsoft Excel 11 Object Library, and
press OK.
Obviously if you have bigger version of Excel 11 use it.
Here is the code, there are comments/regions with the workflow of it. You should use using Excel = Microsoft.Office.Interop.Excel; as reference
public void ExcelBtn_Click(object sender, EventArgs e)
{
DataSet dst = PrepareData();
byte[] bytes = ExportDataSetToExcel(dst);
Response.ClearContent();
Response.ContentType = "application/msoffice";
Response.AddHeader("Content-Disposition", #"attachment; filename=""ExportedExcel.xlsx"" ");
Response.BinaryWrite(bytes);
Response.End();
}
public static DataSet PrepareData()
{
DataTable badBoysDst = new DataTable("BadBoys");
badBoysDst.Columns.Add("Nr");
badBoysDst.Columns.Add("Name");
badBoysDst.Rows.Add(1, "Me");
badBoysDst.Rows.Add(2, "You");
badBoysDst.Rows.Add(3, "Pepe");
badBoysDst.Rows.Add(4, "Roni");
//Create a Department Table
DataTable goodBoysDst = new DataTable("GoodBoys");
goodBoysDst.Columns.Add("Nr");
goodBoysDst.Columns.Add("Name");
goodBoysDst.Rows.Add("1", "Not me");
goodBoysDst.Rows.Add("2", "Not you");
goodBoysDst.Rows.Add("3", "Quattro");
goodBoysDst.Rows.Add("4", "Stagioni");
DataTable goodBoysDst2 = new DataTable("GoodBoys2");
goodBoysDst2.Columns.Add("Nr");
goodBoysDst2.Columns.Add("Name");
goodBoysDst2.Rows.Add("1", "Not me");
goodBoysDst2.Rows.Add("2", "Not you");
goodBoysDst2.Rows.Add("3", "Quattro");
goodBoysDst2.Rows.Add("4", "Stagioni");
DataTable goodBoysDst3 = new DataTable("GoodBoys3");
goodBoysDst3.Columns.Add("Nr");
goodBoysDst3.Columns.Add("Name");
goodBoysDst3.Rows.Add("1", "Not me");
goodBoysDst3.Rows.Add("2", "Not you");
goodBoysDst3.Rows.Add("3", "Quattro");
goodBoysDst3.Rows.Add("4", "Stagioni");
//Create a DataSet with the existing DataTables
DataSet dst = new DataSet("SchoolBoys");
dst.Tables.Add(badBoysDst);
dst.Tables.Add(goodBoysDst);
dst.Tables.Add(goodBoysDst2);
dst.Tables.Add(goodBoysDst3);
return dst;
}
public static byte[] ExportDataSetToExcel(DataSet dst)
{
#region Create The Excel
Excel.Application excelApp = null;
Excel.Workbook excelWorkBook = null;
try
{
excelApp = new Excel.Application();
if (excelApp == null)
throw new Exception("You can throw custom exception here too");
excelWorkBook = excelApp.Workbooks.Add();
int sheetNr = 1;
foreach (DataTable table in dst.Tables)
{
Excel.Worksheet excelWorkSheet = null;
//Add a new worksheet or reuse first 3 sheets of workbook with the Datatable name
if (sheetNr <= excelWorkBook.Sheets.Count)
{
excelWorkSheet = excelWorkBook.Sheets.get_Item(sheetNr);
}
else
{
excelWorkSheet = excelWorkBook.Sheets.Add(After: excelWorkBook.Sheets[excelWorkBook.Sheets.Count]);
}
excelWorkSheet.Name = table.TableName;
for (int i = 1; i < table.Columns.Count + 1; i++)
{
excelWorkSheet.Cells[1, i] = table.Columns[i - 1].ColumnName;
}
for (int j = 0; j < table.Rows.Count; j++)
{
for (int k = 0; k < table.Columns.Count; k++)
{
excelWorkSheet.Cells[j + 2, k + 1] = table.Rows[j].ItemArray[k].ToString();
}
}
sheetNr += 1;
}
//make first sheet active
excelApp.ActiveWorkbook.Sheets[1].Select();
excelWorkBook.SaveAs(#"c:\temp\DataSetToExcel.xlsx");
}
finally
{
excelWorkBook.Close();
excelApp.Quit();
//you should call GC here because there is memory problem with Interop
GC.Collect();
GC.WaitForPendingFinalizers();
}
#endregion
#region Take byte[] of the excel
byte[] result = null;
using (FileStream fs = new FileStream(#"c:\temp\DataSetToExcel.xlsx", FileMode.Open, FileAccess.Read))
{
BinaryReader reader = new BinaryReader(fs);
result = reader.ReadBytes((int)fs.Length);
}
#endregion
#region Delete the excel from the server
File.Delete(#"c:\temp\DataSetToExcel.xlsx");
#endregion
return result;
}
}
So try to use something established by the community already.This is pretty much full example how to do it with Interop. Personally I prefer to use ACE/JET engines, because there is no memory leaks problems like in the Interop(because of that we are calling the GC in the code). Creation of new sheets with ACE/JET engine is a little bit harder.
I think your string comparison in linq query is a problem..your email address might have different case which could have caused this issue. Try below code
DataTable filteredEducation = dt1.AsEnumerable()
.Where(x => dt.AsEnumerable()
.Any(z => z.Field<string>("Email").Trim().Equals(x.Field<string>("Email").Trim(),StringComparison.CurrentCultureIgnoreCase)))
.CopyToDataTable();
DataTable filteredEmployee = dt2.AsEnumerable()
.Where(x => dt.AsEnumerable()
.Any(z => z.Field<string>("Email").Trim().Equals(x.Field<string>("Email").Trim(),StringComparison.CurrentCultureIgnoreCase)))
.CopyToDataTable();
I had done the same export problem by manual export. First, i need to prepare a responce http responce properly, than add all headers(with rowsapn and colspan attributes) of your tables and then populate data:
//this fun is called after click on export button for example
public void Export(string fileName, GridView gv)
{
try
{
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.AddHeader("content-disposition", string.Format("attachment; filename={0}", String.Format("{0}.xls", fileName)));
HttpContext.Current.Response.AddHeader("Content-Transfer-Encoding", "utf-8");
HttpContext.Current.Response.Buffer = true;
HttpContext.Current.Response.ContentType = "application/vnd.ms-excel";
HttpContext.Current.Response.Write(#"<!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 4.0 Transitional//EN"">");
HttpContext.Current.Response.Charset = "utf-8";//"windows-1251";//
HttpContext.Current.Response.ContentEncoding = System.Text.Encoding.GetEncoding("utf-8");
using (StringWriter sw = new StringWriter())
{
using (HtmlTextWriter htw = new HtmlTextWriter(sw))
{
// Create a table to contain the grid
Table table = new Table();
table.Width = Unit.Percentage(100);
// include the gridline settings
table.GridLines = gv.GridLines;
//header
TableRow r = new TableRow();
TableCell cell = new TableCell()
{
ColumnSpan = 18,
Text = fileName,
BackColor = Color.LightGray,
HorizontalAlign = HorizontalAlign.Center
};
cell.Font.Size = new FontUnit(14);
r.Cells.Add(cell);
table.Rows.Add(r);
GridViewRow row;
int rowSpan = 0;
//second row
row = CreateSecondHeaderRow();
table.Rows.AddAt(1, row);
//first row
row = CreateFirstHeaderRow(row, rowSpan);
table.Rows.AddAt(1, row);
// add each of the data rows to the table
for (int j = 0; j < gv.Rows.Count; j++)
{
//Set the default color
gv.Rows[j].BackColor = System.Drawing.Color.White;
for (int i = 0; i < gv.Rows[j].Cells.Count; i++)
{
gv.Rows[j].Cells[i].BackColor = System.Drawing.Color.White;
gv.Rows[j].Cells[i].Width = gv.Columns[i].ItemStyle.Width;
gv.Rows[j].Cells[i].Font.Size = gv.Columns[i].ItemStyle.Font.Size;
gv.Rows[j].Cells[i].Font.Bold = gv.Columns[i].ItemStyle.Font.Bold;
gv.Rows[j].Cells[i].Font.Italic = gv.Columns[i].ItemStyle.Font.Italic;
//aligh
if (i == 0)
{
gv.Rows[j].Cells[i].Style["text-align"] = "center";
}
else
{
gv.Rows[j].Cells[i].Style["text-align"] = "right";
}
//for alternate
if (j % 2 != 1) gv.Rows[j].Cells[i].BackColor = Color.LightSteelBlue;
}
table.Rows.Add(gv.Rows[j]);
}
table.RenderControl(htw);
// render the htmlwriter into the response
HttpContext.Current.Response.Write(sw);
HttpContext.Current.Response.Flush();
HttpContext.Current.Response.End();
}
}
}
catch (Exception ex)
{
this._hasError = true;
ShowError(ex);
}
}
private TableHeaderCell CreateHeaderCell(string text = null, int rowSpan = 0, int columnSpan = 0, Color backColor = default(Color), Color foreColor = default(Color))
{
if (object.Equals(backColor, default(Color))) backColor = Color.LightGray;
if (object.Equals(foreColor, default(Color))) foreColor = Color.Black;
return new TableHeaderCell
{
RowSpan = rowSpan,
ColumnSpan = columnSpan,
Text = text,
BackColor = backColor
};
}
private GridViewRow CreateFirstHeaderRow(GridViewRow row, int rowSpan)
{
row = new GridViewRow(0, 0, DataControlRowType.Header, DataControlRowState.Insert);
TableHeaderCell cell = CreateHeaderCell("Surplus %");
row.Controls.Add(cell);
cell = CreateHeaderCell("The date", columnSpan: 2);
row.Controls.Add(cell);
if (this.WithQuantity)
{
cell = CreateHeaderCell("Total Quantity", 2 + rowSpan, backColor: Color.Yellow);
row.Controls.Add(cell);
}
cell = CreateHeaderCell("Total Amount", 2 + rowSpan);
row.Controls.Add(cell);
cell = CreateHeaderCell("Has elapsed periods from start", columnSpan: (this.WithQuantity ? (SurplusUtil.TheColumnsNumbers * 2) : SurplusUtil.TheColumnsNumbers));
row.Controls.Add(cell);
if (this.WithQuantity)
{
cell = CreateHeaderCell("Quantity <br style='mso-data-placement:same-cell;' /> surplus", 2 + rowSpan, backColor: Color.Yellow);
row.Controls.Add(cell);
}
cell = CreateHeaderCell("Principal <br style='mso-data-placement:same-cell;' /> surplus", 2 + rowSpan);
row.Controls.Add(cell);
return row;
}
private GridViewRow CreateSecondHeaderRow()
{
GridViewRow row = new GridViewRow(0, 0, DataControlRowType.Header, DataControlRowState.Insert);
TableHeaderCell cell = CreateHeaderCell("Period number", rowSpan: ((this.WithQuantity) ? 2 : 0));
row.Controls.Add(cell);
cell = CreateHeaderCell("from", rowSpan: ((this.WithQuantity) ? 2 : 0));
row.Controls.Add(cell);
cell = CreateHeaderCell("to", rowSpan: ((this.WithQuantity) ? 2 : 0));
row.Controls.Add(cell);
for (int i = 0; i < SurplusUtil.TheColumnsNumbers; i++)
{
cell = CreateHeaderCell(i.ToString(),
columnSpan: ((this.WithQuantity) ? 2 : 0),
backColor: System.Drawing.Color.FromArgb(198, 239, 206),
foreColor: System.Drawing.Color.FromArgb(0, 97, 0));
row.Controls.Add(cell);
}
return row;
}

EPPlus - Named Range is not populated

I'm using EPPLus to open Excel Spreadsheets and trying to read from a named range. The Named Range is Empty.
Am I using this wrong, or is it a problem with EPPlus
Code
var package = new ExcelPackage();
using (var stream = File.OpenRead(tmpExcel))
{
package.Load(stream);
}
var worksheet = package.Workbook.Worksheets["Common-Lookup"];
using (ExcelNamedRange namedRange = worksheet.Names["LupVerticalSettings"])
{
for (var row = namedRange.Start.Row; row <= namedRange.End.Row; row++)
{
for (var col = namedRange.Start.Column; col <= namedRange.End.Column; col++)
{
_.Nv(worksheet.Cells[row, col].Address, worksheet.Cells[row, col].Text);
//worksheet.Cells[rowIndex, columnIndex].Value = "no more hair pulling";
}
}
}
The Excel looks like this
Empty Named Range
I solved my problem, I'll put answer here for anyone that may need it in the future
var package = new ExcelPackage();
using (var stream = File.OpenRead(tmpExcel))
{
package.Load(stream);
}
var worksheet = package.Workbook.Worksheets["Common-Lookup"];
// Access Named Ranges from the ExcelWorkbook instead of ExcelWorksheet
//using (ExcelNamedRange namedRange = worksheet.Names["LupVerticalSettings"])
// use package.Workbook.Names instead of worksheet.Names
using (ExcelNamedRange namedRange = package.Workbook.Names["LupVerticalSettings"])
{
for (var row = namedRange.Start.Row; row <= namedRange.End.Row; row++)
{
for (var col = namedRange.Start.Column; col <= namedRange.End.Column; col++)
{
_.Nv(worksheet.Cells[row, col].Address, worksheet.Cells[row, col].Text);
//worksheet.Cells[rowIndex, columnIndex].Value = "no more hair pulling";
}
}
}

Export DataTable to Excel with Open Xml SDK in c#

My program have ability to export some data and DataTable to Excel file (template)
In the template I insert the data to some placeholders. It's works very good, but I need to insert a DataTable too...
My sample code:
using (Stream OutStream = new MemoryStream())
{
// read teamplate
using (var fileStream = File.OpenRead(templatePath))
fileStream.CopyTo(OutStream);
// exporting
Exporting(OutStream);
// to start
OutStream.Seek(0L, SeekOrigin.Begin);
// out
using (var resultFile = File.Create(resultPath))
OutStream.CopyTo(resultFile);
Next method to exporting
private void Exporting(Stream template)
{
using (var workbook = SpreadsheetDocument.Open(template, true, new OpenSettings { AutoSave = true }))
{
// Replace shared strings
SharedStringTablePart sharedStringsPart = workbook.WorkbookPart.SharedStringTablePart;
IEnumerable<Text> sharedStringTextElements = sharedStringsPart.SharedStringTable.Descendants<Text>();
DoReplace(sharedStringTextElements);
// Replace inline strings
IEnumerable<WorksheetPart> worksheetParts = workbook.GetPartsOfType<WorksheetPart>();
foreach (var worksheet in worksheetParts)
{
DoReplace(worksheet.Worksheet.Descendants<Text>());
}
int z = 40;
foreach (System.Data.DataRow row in ExcelWorkXLSX.ToOut.Rows)
{
for (int i = 0; i < row.ItemArray.Count(); i++)
{
ExcelWorkXLSX.InsertText(workbook, row.ItemArray.ElementAt(i).ToString(), getColumnName(i), Convert.ToUInt32(z)); }
z++;
}
}
}
}
But this fragment to output DataTable slooooooooooooooooooooooowwwwwww...
How can I export DataTable to Excel fast and truly?
I wrote this quick example. It works for me. I only tested it with one dataset with one table inside, but I guess that may be enough for you.
Take into consideration that I treated all cells as String (not even SharedStrings). If you want to use SharedStrings you might need to tweak my sample a bit.
Edit: To make this work it is necessary to add WindowsBase and DocumentFormat.OpenXml references to project.
Enjoy,
private void ExportDataSet(DataSet ds, string destination)
{
using (var workbook = SpreadsheetDocument.Create(destination, DocumentFormat.OpenXml.SpreadsheetDocumentType.Workbook))
{
var workbookPart = workbook.AddWorkbookPart();
workbook.WorkbookPart.Workbook = new DocumentFormat.OpenXml.Spreadsheet.Workbook();
workbook.WorkbookPart.Workbook.Sheets = new DocumentFormat.OpenXml.Spreadsheet.Sheets();
foreach (System.Data.DataTable table in ds.Tables) {
var sheetPart = workbook.WorkbookPart.AddNewPart<WorksheetPart>();
var sheetData = new DocumentFormat.OpenXml.Spreadsheet.SheetData();
sheetPart.Worksheet = new DocumentFormat.OpenXml.Spreadsheet.Worksheet(sheetData);
DocumentFormat.OpenXml.Spreadsheet.Sheets sheets = workbook.WorkbookPart.Workbook.GetFirstChild<DocumentFormat.OpenXml.Spreadsheet.Sheets>();
string relationshipId = workbook.WorkbookPart.GetIdOfPart(sheetPart);
uint sheetId = 1;
if (sheets.Elements<DocumentFormat.OpenXml.Spreadsheet.Sheet>().Count() > 0)
{
sheetId =
sheets.Elements<DocumentFormat.OpenXml.Spreadsheet.Sheet>().Select(s => s.SheetId.Value).Max() + 1;
}
DocumentFormat.OpenXml.Spreadsheet.Sheet sheet = new DocumentFormat.OpenXml.Spreadsheet.Sheet() { Id = relationshipId, SheetId = sheetId, Name = table.TableName };
sheets.Append(sheet);
DocumentFormat.OpenXml.Spreadsheet.Row headerRow = new DocumentFormat.OpenXml.Spreadsheet.Row();
List<String> columns = new List<string>();
foreach (System.Data.DataColumn column in table.Columns) {
columns.Add(column.ColumnName);
DocumentFormat.OpenXml.Spreadsheet.Cell cell = new DocumentFormat.OpenXml.Spreadsheet.Cell();
cell.DataType = DocumentFormat.OpenXml.Spreadsheet.CellValues.String;
cell.CellValue = new DocumentFormat.OpenXml.Spreadsheet.CellValue(column.ColumnName);
headerRow.AppendChild(cell);
}
sheetData.AppendChild(headerRow);
foreach (System.Data.DataRow dsrow in table.Rows)
{
DocumentFormat.OpenXml.Spreadsheet.Row newRow = new DocumentFormat.OpenXml.Spreadsheet.Row();
foreach (String col in columns)
{
DocumentFormat.OpenXml.Spreadsheet.Cell cell = new DocumentFormat.OpenXml.Spreadsheet.Cell();
cell.DataType = DocumentFormat.OpenXml.Spreadsheet.CellValues.String;
cell.CellValue = new DocumentFormat.OpenXml.Spreadsheet.CellValue(dsrow[col].ToString()); //
newRow.AppendChild(cell);
}
sheetData.AppendChild(newRow);
}
}
}
}
eburgos, I've modified your code slightly because when you have multiple datatables in your dataset it was just overwriting them in the spreadsheet so you were only left with one sheet in the workbook. I basically just moved the part where the workbook is created out of the loop. Here is the updated code.
private void ExportDSToExcel(DataSet ds, string destination)
{
using (var workbook = SpreadsheetDocument.Create(destination, DocumentFormat.OpenXml.SpreadsheetDocumentType.Workbook))
{
var workbookPart = workbook.AddWorkbookPart();
workbook.WorkbookPart.Workbook = new DocumentFormat.OpenXml.Spreadsheet.Workbook();
workbook.WorkbookPart.Workbook.Sheets = new DocumentFormat.OpenXml.Spreadsheet.Sheets();
uint sheetId = 1;
foreach (DataTable table in ds.Tables)
{
var sheetPart = workbook.WorkbookPart.AddNewPart<WorksheetPart>();
var sheetData = new DocumentFormat.OpenXml.Spreadsheet.SheetData();
sheetPart.Worksheet = new DocumentFormat.OpenXml.Spreadsheet.Worksheet(sheetData);
DocumentFormat.OpenXml.Spreadsheet.Sheets sheets = workbook.WorkbookPart.Workbook.GetFirstChild<DocumentFormat.OpenXml.Spreadsheet.Sheets>();
string relationshipId = workbook.WorkbookPart.GetIdOfPart(sheetPart);
if (sheets.Elements<DocumentFormat.OpenXml.Spreadsheet.Sheet>().Count() > 0)
{
sheetId =
sheets.Elements<DocumentFormat.OpenXml.Spreadsheet.Sheet>().Select(s => s.SheetId.Value).Max() + 1;
}
DocumentFormat.OpenXml.Spreadsheet.Sheet sheet = new DocumentFormat.OpenXml.Spreadsheet.Sheet() { Id = relationshipId, SheetId = sheetId, Name = table.TableName };
sheets.Append(sheet);
DocumentFormat.OpenXml.Spreadsheet.Row headerRow = new DocumentFormat.OpenXml.Spreadsheet.Row();
List<String> columns = new List<string>();
foreach (DataColumn column in table.Columns)
{
columns.Add(column.ColumnName);
DocumentFormat.OpenXml.Spreadsheet.Cell cell = new DocumentFormat.OpenXml.Spreadsheet.Cell();
cell.DataType = DocumentFormat.OpenXml.Spreadsheet.CellValues.String;
cell.CellValue = new DocumentFormat.OpenXml.Spreadsheet.CellValue(column.ColumnName);
headerRow.AppendChild(cell);
}
sheetData.AppendChild(headerRow);
foreach (DataRow dsrow in table.Rows)
{
DocumentFormat.OpenXml.Spreadsheet.Row newRow = new DocumentFormat.OpenXml.Spreadsheet.Row();
foreach (String col in columns)
{
DocumentFormat.OpenXml.Spreadsheet.Cell cell = new DocumentFormat.OpenXml.Spreadsheet.Cell();
cell.DataType = DocumentFormat.OpenXml.Spreadsheet.CellValues.String;
cell.CellValue = new DocumentFormat.OpenXml.Spreadsheet.CellValue(dsrow[col].ToString()); //
newRow.AppendChild(cell);
}
sheetData.AppendChild(newRow);
}
}
}
}
I also wrote a C#/VB.Net "Export to Excel" library, which uses OpenXML and (more importantly) also uses OpenXmlWriter, so you won't run out of memory when writing large files.
Full source code, and a demo, can be downloaded here:
Export to Excel
It's dead easy to use.
Just pass it the filename you want to write to, and a DataTable, DataSet or List<>.
CreateExcelFile.CreateExcelDocument(myDataSet, "MyFilename.xlsx");
And if you're calling it from an ASP.Net application, pass it the HttpResponse to write the file out to.
CreateExcelFile.CreateExcelDocument(myDataSet, "MyFilename.xlsx", Response);
I wrote my own export to Excel writer because nothing else quite met my needs. It is fast and allows for substantial formatting of the cells. You can review it at
https://openxmlexporttoexcel.codeplex.com/
I hope it helps.
You could try taking a look at this libary. I've used it for one of my projects and found it very easy to work with, reliable and fast (I only used it for exporting data).
http://epplus.codeplex.com/
You can have a look at my library here. Under the documentation section, you will find how to import a data table.
You just have to write
using (var doc = new SpreadsheetDocument(#"C:\OpenXmlPackaging.xlsx")) {
Worksheet sheet1 = doc.Worksheets.Add("My Sheet");
sheet1.ImportDataTable(ds.Tables[0], "A1", true);
}
Hope it helps!
I tried accepted answer and got message saying generated excel file is corrupted when trying to open. I was able to fix it by doing few modifications like adding below line end of the code.
workbookPart.Workbook.Save();
I have posted full code # Export DataTable to Excel with Open XML in c#
I wanted to add this answer because I used the primary answer from this question as my basis for exporting from a datatable to Excel using OpenXML but then transitioned to OpenXMLWriter when I found it to be much faster than the above method.
You can find the full details in my answer in the link below. My code is in VB.NET though, so you'll have to convert it.
How to export DataTable to Excel

EPPlus - How to use a template

I have recently discovered EPPlus (http://epplus.codeplex.com/).
I have an excel .xlsx file in my project with all the styled column headers.
I read on their site that you can use templates.
Does anyone know how or can provide code sample of how to use my template.xlsx file with EPPlus? I would like to be able to simply load my data into the rows without messing with the headings.
The solution I ended up going with:
using System.IO;
using System.Reflection;
using OfficeOpenXml;
//Create a stream of .xlsx file contained within my project using reflection
Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("EPPlusTest.templates.VendorTemplate.xlsx");
//EPPlusTest = Namespace/Project
//templates = folder
//VendorTemplate.xlsx = file
//ExcelPackage has a constructor that only requires a stream.
ExcelPackage pck = new OfficeOpenXml.ExcelPackage(stream);
After that you can use all the methods of ExcelPackage that you want on an .xlsx file loaded from a template.
To create a new package, you can provide a stream template:
// templateName = the name of .xlsx file
// result = stream to write the resulting xlsx to
using (var source = System.IO.File.OpenRead(templateName))
using (var excel = new OfficeOpenXml.ExcelPackage(result, source)) {
// Fill cells here
// Leave headers etc as is
excel.Save();
}
//This is my Implementation for EPPlus. // may be it helps.
class EPPlus
{
FileInfo newFile;
FileInfo templateFile;
DataSet _ds;
ExcelPackage xlPackage;
public string _ErrorMessage;
public EPPlus(string filePath, string templateFilePath)
{
newFile = new FileInfo(#filePath);
templateFile = new FileInfo(#templateFilePath);
_ds = GetDataTables(); /* DataTables */
_ErrorMessage = string.Empty;
CreateFileWithTemplate();
}
private bool CreateFileWithTemplate()
{
try
{
_ErrorMessage = string.Empty;
using (xlPackage = new ExcelPackage(newFile, templateFile))
{
int i = 1;
foreach (DataTable dt in _ds.Tables)
{
AddSheetWithTemplate(xlPackage, dt, i);
i++;
}
///* Set title, Author.. */
//xlPackage.Workbook.Properties.Title = "Title: Office Open XML Sample";
//xlPackage.Workbook.Properties.Author = "Author: Muhammad Mubashir.";
////xlPackage.Workbook.Properties.SetCustomPropertyValue("EmployeeID", "1147");
//xlPackage.Workbook.Properties.Comments = "Sample Record Details";
//xlPackage.Workbook.Properties.Company = "TRG Tech.";
///* Save */
xlPackage.Save();
}
return true;
}
catch (Exception ex)
{
_ErrorMessage = ex.Message.ToString();
return false;
}
}
/// <summary>
/// This AddSheet method generates a .xlsx Sheet with your provided Template file, //DataTable and SheetIndex.
/// </summary>
public static void AddSheetWithTemplate(ExcelPackage xlApp, DataTable dt, int SheetIndex)
{
string _SheetName = string.Format("Sheet{0}", SheetIndex.ToString());
ExcelWorksheet worksheet;
/* WorkSheet */
if (SheetIndex == 0)
{
worksheet = xlApp.Workbook.Worksheets[SheetIndex + 1]; // add a new worksheet to the empty workbook
}
else
{
worksheet = xlApp.Workbook.Worksheets[SheetIndex]; // add a new worksheet to the empty workbook
}
if (worksheet == null)
{
worksheet = xlApp.Workbook.Worksheets.Add(_SheetName); // add a new worksheet to the empty workbook
}
else
{
}
/* Load the datatable into the sheet, starting from cell A1. Print the column names on row 1 */
worksheet.Cells["A1"].LoadFromDataTable(dt, true);
}
private static void AddSheet(ExcelPackage xlApp, DataTable dt, int Index, string sheetName)
{
string _SheetName = string.Empty;
if (string.IsNullOrEmpty(sheetName) == true)
{
_SheetName = string.Format("Sheet{0}", Index.ToString());
}
else
{
_SheetName = sheetName;
}
/* WorkSheet */
ExcelWorksheet worksheet = xlApp.Workbook.Worksheets[_SheetName]; // add a new worksheet to the empty workbook
if (worksheet == null)
{
worksheet = xlApp.Workbook.Worksheets.Add(_SheetName); // add a new worksheet to the empty workbook
}
else
{
}
/* Load the datatable into the sheet, starting from cell A1. Print the column names on row 1 */
worksheet.Cells["A1"].LoadFromDataTable(dt, true);
int rowCount = dt.Rows.Count;
int colCount = dt.Columns.Count;
#region Set Column Type to Date using LINQ.
/*
IEnumerable<int> dateColumns = from DataColumn d in dt.Columns
where d.DataType == typeof(DateTime) || d.ColumnName.Contains("Date")
select d.Ordinal + 1;
foreach (int dc in dateColumns)
{
xlSheet.Cells[2, dc, rowCount + 1, dc].Style.Numberformat.Format = "dd/MM/yyyy";
}
*/
#endregion
#region Set Column Type to Date using LOOP.
/* Set Column Type to Date. */
for (int i = 0; i < dt.Columns.Count; i++)
{
if ((dt.Columns[i].DataType).FullName == "System.DateTime" && (dt.Columns[i].DataType).Name == "DateTime")
{
//worksheet.Cells[2,4] .Style.Numberformat.Format = "yyyy-mm-dd h:mm"; //OR "yyyy-mm-dd h:mm" if you want to include the time!
worksheet.Column(i + 1).Style.Numberformat.Format = "dd/MM/yyyy h:mm"; //OR "yyyy-mm-dd h:mm" if you want to include the time!
worksheet.Column(i + 1).Width = 25;
}
}
#endregion
//(from DataColumn d in dt.Columns select d.Ordinal + 1).ToList().ForEach(dc =>
//{
// //background color
// worksheet.Cells[1, 1, 1, dc].Style.Fill.PatternType = ExcelFillStyle.Solid;
// worksheet.Cells[1, 1, 1, dc].Style.Fill.BackgroundColor.SetColor(System.Drawing.Color.LightYellow);
// //border
// worksheet.Cells[1, dc, rowCount + 1, dc].Style.Border.Top.Style = ExcelBorderStyle.Thin;
// worksheet.Cells[1, dc, rowCount + 1, dc].Style.Border.Right.Style = ExcelBorderStyle.Thin;
// worksheet.Cells[1, dc, rowCount + 1, dc].Style.Border.Bottom.Style = ExcelBorderStyle.Thin;
// worksheet.Cells[1, dc, rowCount + 1, dc].Style.Border.Left.Style = ExcelBorderStyle.Thin;
// worksheet.Cells[1, dc, rowCount + 1, dc].Style.Border.Top.Color.SetColor(System.Drawing.Color.LightGray);
// worksheet.Cells[1, dc, rowCount + 1, dc].Style.Border.Right.Color.SetColor(System.Drawing.Color.LightGray);
// worksheet.Cells[1, dc, rowCount + 1, dc].Style.Border.Bottom.Color.SetColor(System.Drawing.Color.LightGray);
// worksheet.Cells[1, dc, rowCount + 1, dc].Style.Border.Left.Color.SetColor(System.Drawing.Color.LightGray);
//});
/* Format the header: Prepare the range for the column headers */
string cellRange = "A1:" + Convert.ToChar('A' + colCount - 1) + 1;
using (ExcelRange rng = worksheet.Cells[cellRange])
{
rng.Style.Font.Bold = true;
rng.Style.Fill.PatternType = ExcelFillStyle.Solid; //Set Pattern for the background to Solid
rng.Style.Fill.BackgroundColor.SetColor(Color.FromArgb(79, 129, 189)); //Set color to dark blue
rng.Style.Font.Color.SetColor(Color.White);
}
/* Header Footer */
worksheet.HeaderFooter.OddHeader.CenteredText = "Header: Tinned Goods Sales";
worksheet.HeaderFooter.OddFooter.RightAlignedText = string.Format("Footer: Page {0} of {1}", ExcelHeaderFooter.PageNumber, ExcelHeaderFooter.NumberOfPages); // add the page number to the footer plus the total number of pages
}
}// class End.
I use Vb.net, here is what I did:
VB
Imports OfficeOpenXml
Dim existingFile As New FileInfo("C:\OldFileLocation\File.xlsx")
Dim fNewFile As New FileInfo("C:\NewFileLocation\File.xlsx")
Using MyExcel As New ExcelPackage(existingFile)
Dim MyWorksheet As ExcelWorksheet = MyExcel.Workbook.Worksheets("ExistingSheetName")
MyWorksheet.Cells("A1").Value = "Hello"
'Add additional info here
MyExcel.SaveAs(fNewFile)
End Using
Posible C# (I did not test)
FileInfo existingFile = new FileInfo("C:\\OldFileLocation\\File.xlsx");
FileInfo fNewFile = new FileInfo("C:\\NewFileLocation\\File.xlsx");
using (ExcelPackage MyExcel = new ExcelPackage(existingFile)) {
ExcelWorksheet MyWorksheet = MyExcel.Workbook.Worksheets["ExistingSheetName"];
MyWorksheet.Cells["A1"].Value = "Hello";
//Add additional info here
MyExcel.SaveAs(fNewFile);
}
Response.Clear();
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("content-disposition", "attachment;filename=" + HttpUtility.UrlEncode("Logs.xlsx", System.Text.Encoding.UTF8));
using (ExcelPackage pck = new ExcelPackage())
{
ExcelWorksheet ws = pck.Workbook.Worksheets.Add("Logs");
ws.Cells["A1"].LoadFromDataTable(dt, true);
var ms = new System.IO.MemoryStream();
pck.SaveAs(ms);
ms.WriteTo(Response.OutputStream);
}

Categories

Resources