Interop - Speed of inserting into open excel instance - c#

I am finding a running instance of excel and inserting data into one of the worksheets. Everything works, however inserting data is very slow. ~0.25 seconds for each cell.
Is there any way I can make this faster?
I have been looking for an approach that doesn't go cell by cell, but haven't found anything.
My code:
using System;
using System.Data;
using Microsoft.Office.Interop.Excel;
using Application = Microsoft.Office.Interop.Excel.Application;
using DataTable = System.Data.DataTable;
[STAThread]
static void Main()
{
Application xlApp = null;
try
{
xlApp = (Application)System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application");
}
catch (Exception)//Excel not open
{
return;
}
xlApp.Visible = true;
var wb = xlApp.ActiveWorkbook;
Worksheet ws = null;
try
{
ws = (Worksheet)wb.Worksheets["SomeSheet"];
}
catch (Exception e)
{
return;
}
if (ws == null)
{
return;
}
var dt = new DataTable();
dt = new DataTable();
dt.Clear();
dt.Columns.Add("Col1");
dt.Columns.Add("Col2");
for (int ii = 0; ii < 20; ii++)
{
DataRow row = dt.NewRow();
row["Col1"] = "xxxx";
row["Col2"] = "yyyy";
dt.Rows.Add(row);
}
//Header
for (int i = 0; i < dt.Columns.Count; i++)
{
ws.Cells[1, i + 1] = dt.Columns[i].ColumnName;
}
//Data
for (int i = 0; i < dt.Rows.Count; i++)
{
for (int j = 0; j < dt.Columns.Count; j++)
{
ws.Cells[i + 2, j + 1] = dt.Rows[i][j];
}
}
}

Have you tried using a Range? Assigning a Array to the range ensuring the Range and Array are the same length should work.
https://msdn.microsoft.com/en-us/library/microsoft.office.tools.excel.worksheet.range.aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1

Related

How to read Excel to Datatable using NPOI C#

I'm Trying to read Excel to DataTable using NPOI.Every thing working fine but only issue is If we have any Column cell is empty in that row it is not reading .In Excel i have 4 row's with (each row have some empty values for cells).
Excel File Image : enter image description here
After Reading That Excel To data table :enter image description here
I want like this in data table
private DataTable GetDataTableFromExcel(String Path)
{
XSSFWorkbook wb;
XSSFSheet sh;
String Sheet_name;
using (var fs = new FileStream(Path, FileMode.Open, FileAccess.Read))
{
wb = new XSSFWorkbook(fs);
Sheet_name = wb.GetSheetAt(0).SheetName; //get first sheet name
}
DataTable DT = new DataTable();
DT.Rows.Clear();
DT.Columns.Clear();
// get sheet
sh = (XSSFSheet)wb.GetSheet(Sheet_name);
int i = 0;
while (sh.GetRow(i) != null)
{
// add neccessary columns
if (DT.Columns.Count < sh.GetRow(i).Cells.Count)
{
for (int j = 0; j < sh.GetRow(i).Cells.Count; j++)
{
DT.Columns.Add("", typeof(string));
}
}
// add row
DT.Rows.Add();
// write row value
for (int j = 0; j < sh.GetRow(i).Cells.Count; j++)
{
var cell = sh.GetRow(i).GetCell(j);
DT.Rows[i][j] = sh.GetRow(i).GetCell(j);
}
i++;
}
return DT;
}
Plese hlp me.
you may have to try something along this line. its workable code to read the excel using NPOI.
// read the current row data
XSSFRow headerRow = (XSSFRow)sheet.GetRow(0);
// LastCellNum is the number of cells of current rows
int cellCount = headerRow.LastCellNum;
// LastRowNum is the number of rows of current table
int rowCount = sheet.LastRowNum + 1;
bool isBlanKRow = false;
//Start reading data after first row(header row) of excel sheet.
for (int i = (sheet.FirstRowNum + 1); i < rowCount; i++)
{
XSSFRow row = (XSSFRow)sheet.GetRow(i);
DataRow dataRow = dt.NewRow();
isBlanKRow = true;
try
{
for (int j = row.FirstCellNum; j < cellCount; j++)
{
if (null != row.GetCell(j) && !string.IsNullOrEmpty(row.GetCell(j).ToString()) && !string.IsNullOrWhiteSpace(row.GetCell(j).ToString()))
{
dataRow[j] = row.GetCell(j).ToString();
isBlanKRow = false;
}
}
}
catch (Exception Ex)
{
}
if (!isBlanKRow)
{
dt.Rows.Add(dataRow);
}
}

Create an Excel file with the Excel interop class from SQL Database

I want to create an Excel file with the Excel interop class from the SQL Database. With SQL query, I had previously transferred the dataset into dataGridview. The result of the query is the data. However, I can't Print this data from the SQL database to Excel cells with the dataset.
private void linkLabel10_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
using (var fbd = new FolderBrowserDialog())
{
DialogResult result = fbd.ShowDialog();
if (result == DialogResult.OK && !string.IsNullOrWhiteSpace(fbd.SelectedPath))
{
string fileTest = fbd.SelectedPath.ToString() + "\\"+ comboBox1.Text.Substring(0, comboBox1.Text.IndexOf("*") - 1) +"-"+ comboBox2.Text.Substring(0, comboBox2.Text.IndexOf("(") - 1) +"-"+ comboBox2.Text.Substring(comboBox2.Text.IndexOf("(") + 1, 10) + ".xlsx";
MessageBox.Show(fileTest);
if (File.Exists(fileTest))
{
File.Delete(fileTest);
}
SqlDataAdapter da = new SqlDataAdapter("select PersonelKodu= ztSinifEgitimiDurum.KisiID, İsim= dbo.cdCurrAcc.FullName, Katılım = CASE WHEN Katilim = 1 THEN 'Katıldı' ELSE 'Katılmadı' END, ztSinifEgitimiDurum.ID FROM dbo.ztSinifEgitimiDurum INNER JOIN dbo.cdCurrAcc ON cdCurrAcc.CurrAccCode = ztSinifEgitimiDurum.KisiID AND cdCurrAcc.CurrAccTypeCode = ztSinifEgitimiDurum.KisiTipiID WHERE AtamaID =" + AtamaIDBul(), baglan);
DataSet ds = new DataSet();
Excel.Application Excel;
Excel.Worksheet excelWorkSheet;
Excel.Workbook excelWorkBook;
Excel = new Excel.Application();
excelWorkBook = Excel.Workbooks.Add();
excelWorkSheet = (Excel.Worksheet)excelWorkBook.Worksheets.get_Item(1);
excelWorkSheet.Cells[1, 1] = "some value";
excelWorkSheet.Name = "Ali";
foreach (DataTable table in ds.Tables)
{
for (int i = 1; i < table.Columns.Count + 1; i++)
{
excelWorkSheet.Cells[1, i] = table.Columns[i - 1].ColumnName;
MessageBox.Show(table.Columns[i - 1].ColumnName.ToString());
}
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();
}
}
}
// excelWorkSheet.Cells[1, 1] = "some value";
excelWorkBook.SaveAs(fileTest);
excelWorkBook.Close();
Excel.Quit();
}
}
}
You forgot to fill your DataSet, add this line
da.Fill(ds); after DataSet ds = new DataSet();
You can try this code to fill your sheet with values from a table:
ws.Activate();
foreach (DataTable dt in ds.Tables)
{
// Add column headers from the datatable
for (int Idx = 0; Idx < dt.Columns.Count; Idx++)
{
ws.Range["A1"].Offset[0, Idx].Value = dt.Columns[Idx].ColumnName;
}
// add data rows
for (int Idx = 0; Idx < dt.Rows.Count; Idx++)
{ // hey I did not invent this line of code, I found it somewhere on CodeProject.
// It works to add the whole row at once
ws.Range["A2"].Offset[Idx].Resize[1, dt.Columns.Count].Value = dt.Rows[Idx].ItemArray;
}
...
But you will have to add more worksheets for individual tables in case there is more than one table in your DataSet.
It is based on this article.

Export Sql data to Excel , Store long int to string in excel

I have this working C# code to export Sql Server data to excel. The problem is one column contains long int and it appears in excel as 6.71524E+11. So I understand that we have to convert it as string in excel.
How to implement that in my code? Examples would be appreciated.
public static void ExportToExcel(DataTable dt)
{
try
{
string conString = "Data Source=DELL\\SQLSERVER1;Trusted_Connection=True;DATABASE=Camo;CONNECTION RESET=FALSE";
SqlConnection sqlCon = new SqlConnection(conString);
sqlCon.Open();
SqlDataAdapter da = new SqlDataAdapter("select TOP 10000 LocalSKU,ItemName, QOH,Price,Discontinued,Barcode,Integer2,Integer3,SalePrice,SaleOn,Price2 from dbo.Inventory", sqlCon);
System.Data.DataTable dtMainSQLData = new System.Data.DataTable();
da.Fill(dtMainSQLData);
DataColumnCollection dcCollection = dtMainSQLData.Columns;
// Export Data into EXCEL Sheet
Microsoft.Office.Interop.Excel.ApplicationClass ExcelApp = new Microsoft.Office.Interop.Excel.ApplicationClass();
ExcelApp.Application.Workbooks.Add(Type.Missing);
int i = 1;
int j = 1;
//header row
foreach (DataColumn col in dtMainSQLData.Columns)
{
ExcelApp.Cells[i, j] = col.ColumnName;
j++;
}
i++;
//data rows
foreach (DataRow row in dtMainSQLData.Rows)
{
for (int k = 1; k < dtMainSQLData.Columns.Count + 1; k++)
{
ExcelApp.Cells[i, k] = row[k - 1].ToString();
}
i++;
}
ExcelApp.ActiveWorkbook.SaveCopyAs("C:/Users/Administrator.CAMO/Downloads/FtpFilesStorage/Export/Sheet1.xlsx");
ExcelApp.ActiveWorkbook.Saved = true;
ExcelApp.Quit();
Console.WriteLine(".xlsx file Exported succssessfully.");
}
This method work for exporting to an Excel file.
private void ExportToExcel(DataTable Tbl, string ExcelFilePath = null)
{
try
{
if (Tbl == null || Tbl.Columns.Count == 0)
throw new Exception("ExportToExcel: Null or empty input table!\n");
// load excel, and create a new workbook
Excel.Application excelApp = new Excel.Application();
//Excel.Workbook ExcelBookServices = excelApp.Workbooks.Add();
excelApp.Workbooks.Add();
// single worksheet
Excel._Worksheet workSheet = (Excel._Worksheet)excelApp.ActiveSheet;
// column headings
for (int i = 0; i < Tbl.Columns.Count; i++)
{
workSheet.Cells[1, (i + 1)] = Tbl.Columns[i].ColumnName;
}
// rows
for (int i = 0; i < Tbl.Rows.Count; i++)
{
// to do: format datetime values before printing
for (int j = 0; j < Tbl.Columns.Count; j++)
{
workSheet.Cells[(i + 2), (j + 1)] = Tbl.Rows[i][j];
}
}
workSheet.Columns.AutoFit();
//workSheet.Columns.Style = "Output";
//Excel.Range cell = ((Excel.Range)workSheet.Cells[Tbl.Rows.Count, Tbl.Columns.Count]);
// check fielpath
if (ExcelFilePath != null && ExcelFilePath != "")
{
try
{
workSheet.SaveAs(ExcelFilePath);
excelApp.Quit();
//File Saved Message
}
catch (Exception ex)
{
//ExportToExcel: Excel file could not be saved! Check filepath Message
}
}
else // no filepath is given
{
excelApp.Visible = true;
}
}
catch (Exception ex)
{
//Error in creating the Excel file
ScriptManager.RegisterStartupScript(this, GetType(), "Message", "ExportToExcel: \n" + ex.Message, true);
}
}
Change the for loop to
int i = 1;
int j = 1;
//header row
foreach (DataColumn col in dtMainSQLData.Columns)
{
ExcelApp.Cells[i, j] = col.ColumnName;
j++;
ExcelApp.Rows.AutoFit();
ExcelApp.Columns.AutoFit();
}
i++;
Console.Write("Progressing......65% \n Wait for around 8 minutes \r");
//data rows
foreach (DataRow row in dtMainSQLData.Rows)
{
for (int k = 1; k < dtMainSQLData.Columns.Count + 1; k++)
{
ExcelApp.Cells[i, k] = "'" + row[k - 1].ToString();
}
i++;
ExcelApp.Columns.AutoFit();
ExcelApp.Rows.AutoFit();
}
use the property NumberFormat:
MyWorkBook.NumberFormat = "#";

NPOI with ASP.NET (C#)

I am trying to get NPOI to work with ASP.NET (C#) and I want to read an excel file and put it in a DataSet. Here is the code I attempted:
public static DataTable getExcelData(string FileName, string strSheetName)
{
DataTable dt = new DataTable();
HSSFWorkbook hssfworkbook;
using (FileStream file = new FileStream(FileName, FileMode.Open, FileAccess.Read))
{
hssfworkbook = new HSSFWorkbook(file);
}
ISheet sheet = hssfworkbook.GetSheet(strSheetName);
System.Collections.IEnumerator rows = sheet.GetRowEnumerator();
while (rows.MoveNext())
{
IRow row = (HSSFRow)rows.Current;
if (dt.Columns.Count == 0)
{
for (int j = 0; j < row.LastCellNum; j++)
{
dt.Columns.Add(row.GetCell(j).ToString());
}
continue;
}
DataRow dr = dt.NewRow();
for (int i = 0; i < row.LastCellNum; i++)
{
ICell cell = row.GetCell(i);
if (cell == null)
{
dr[i] = null;
}
else
{
dr[i] = cell.ToString();
}
}
dt.Rows.Add(dr);
}
return dt;
}
The Error that I get is
+ $exception {"Object reference not set to an instance of an object."} System.Exception {System.NullReferenceException}
The odd thing is that this actually works with 2 excel files that I have, but when I put in a third one it crashes with that error.
This returns null if strSheetName isn't found:
ISheet sheet = hssfworkbook.GetSheet(strSheetName);
try:
for( int iSheet = 0; iSheet < hssfworkbook.NumberOfSheets; ++iSheet )
{
ISheet sheet = hssfworkbook.GetSheetAt(iSheet); // could cast to HSSFSheet
String strSheetNameActual = sheet.SheetName;
}
Then figure out how you want to compare strSheetName to strSheetNameActual or which sheets you want to process and how.
Try using this:
for (int j = row.FirstCellNum; j < row.LastCellNum; j++)
and
for (int i = row.FirstCellNum; i < row.LastCellNum; i++)
Instead of:
for (int j = 0; j < row.LastCellNum; j++)
and
for (int i = 0; i < row.LastCellNum; i++)
Also, make sure that you manage the case when the cells on the first row are null:
if (dt.Columns.Count == 0)
{
int empty = 0;
for (int j = row.FirstCellNum; j < row.LastCellNum; j++)
{
ICell cell = row.GetCell(j);
if (cell == null)
{
dt.Columns.Add(String.Format("emptyColumnName_{0}", empty++));
}
else
{
dt.Columns.Add(row.GetCell(j).ToString());
}
}
continue;
}
If you always want to read from the first sheet (probably, to get rid of the second method parameter, the sheet name, which is also the cause of your error), you may use:
// rest of the method's code
ISheet sheet = hssfworkbook.GetSheetAt(0);
if (sheet == null)
return dt;
var rows = sheet.GetRowEnumerator();
// rest of the method's code

How to export data table with proper formatting and write text in Excel using C# Microsoft.Office.Interop.Excel?

I'm using this function to export a data table into excel.
protected void ExportExcel(System.Data.DataTable dt)
{
if (dt == null||dt.Rows.Count==0) return;
Microsoft.Office.Interop.Excel.Application xlApp =
new Microsoft.Office.Interop.Excel.Application();
if (xlApp == null)
{
return;
}
System.Globalization.CultureInfo CurrentCI =
System.Threading.Thread.CurrentThread.CurrentCulture;
System.Threading.Thread.CurrentThread.CurrentCulture =
new System.Globalization.CultureInfo("en-US");
Microsoft.Office.Interop.Excel.Workbooks workbooks = xlApp.Workbooks;
Microsoft.Office.Interop.Excel.Workbook workbook =
workbooks.Add(Microsoft.Office.Interop.Excel.XlWBATemplate.xlWBATWorksheet);
Microsoft.Office.Interop.Excel.Worksheet worksheet =
(Microsoft.Office.Interop.Excel.Worksheet)workbook.Worksheets[1];
Microsoft.Office.Interop.Excel.Range range;
long totalCount = dt.Rows.Count;
long rowRead = 0;
float percent = 0;
for (int i = 0; i < dt.Columns.Count; i++)
{
worksheet.Cells[1, i + 1] = dt.Columns[i].ColumnName;
range = (Microsoft.Office.Interop.Excel.Range)worksheet.Cells[1, i + 1];
range.Interior.ColorIndex = 15;
range.Font.Bold = true;
}
for (int r = 0; r < dt.Rows.Count; r++)
{
for (int i = 0; i < dt.Columns.Count; i++)
{
worksheet.Cells[r + 2, i + 1] = dt.Rows[r][i].ToString();
}
rowRead++;
percent = ((float)(100 * rowRead)) / totalCount;
}
xlApp.Visible = true;
}
As you can see in the resulting excel image, the columns are not properly formatted i.e., the column sizes do not adapt to the databound items. How will I make the excel cells auto-adjust according to the data table?
And I also would like to add some text, maybe a header, for say maybe "System.DateTime.Now" or "data table name". How do i do this?
You should be able to format the columns like so:
Microsoft.Office.Interop.Excel.Range columns = worksheet.UsedRange.Columns;
columns.AutoFit();
You can insert a new row and place the current date as the title like so:
worksheet.Rows[1].Insert();
Excel.Range newRow = worksheet.Rows[1];
Excel.Range newCell = newRow.Cells[1];
newCell.Value = DateTime.Now.ToString("yyyy-MM-dd");
Also add this to your using statements so you don't need to use the fully qualified name every time you use an Excel object:
using Excel = Microsoft.Office.Interop.Excel;
Also, I have tidied up your method, by adding a few using statements to reduce clutter, removing some unnecessary casting and an unnesscessary check for null on the application object, just after it had been assigned:
using Excel = Microsoft.Office.Interop.Excel;
using System.Globalization;
using System.Threading;
using System.Data;
protected void ExportExcel(DataTable dt)
{
if (dt == null || dt.Rows.Count == 0) return;
var xlApp = new Excel.Application();
//Is this used?
CultureInfo CurrentCI = Thread.CurrentThread.CurrentCulture;
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
Excel.Workbooks workbooks = xlApp.Workbooks;
Excel.Range range
Excel.Workbook workbook = workbooks.Add();
Excel.Worksheet worksheet = workbook.Worksheets[1];
long totalCount = dt.Rows.Count;
long rowRead = 0;
float percent = 0;
for (var i = 0; i < dt.Columns.Count; i++)
{
worksheet.Cells[1, i + 1] = dt.Columns[i].ColumnName;
range = worksheet.Cells[1, i + 1];
range.Interior.ColorIndex = 15;
range.Font.Bold = true;
}
for (var r = 0; r < dt.Rows.Count; r++)
{
for (var i = 0; i < dt.Columns.Count; i++)
{
worksheet.Cells[r + 2, i + 1] = dt.Rows[r][i].ToString();
}
rowRead++;
//is this used?
percent = ((float)(100 * rowRead)) / totalCount;
}
Microsoft.Office.Interop.Excel.Range columns = worksheet.UsedRange.Columns;
columns.AutoFit();
worksheet.Rows[1].Insert();
Excel.Range newRow = worksheet.Rows[1];
Excel.Range newCell = newRow.Cells[1];
newCell.Value = DateTime.Now.ToString("yyyy-MM-dd");
xlApp.Visible = true;
}

Categories