Reason to not multithread when writing to multiple excel worksheets - c#

I am working on some code that a developer I replaced wrote. He wrote a lengthy piece of code what writes to multiple excel worksheets on the same excel file. I am thinking about using several background workers to speed up the process of writing to four excel worksheets. Would there be a reason why it would be a good idea to leave all this on one thread? I have used multi-threading before, but not in c# and not writing to excel. I could not find any documentation either way.
Here is the code
xlWorkSheet = xlWorkBook.Worksheets.get_Item(1);
// work order
xlWorkSheet.Cells[4, 4] = nld.s_WorkOrderNumber;
// technician
xlWorkSheet.Cells[6, 4] = nld.s_TechnicianName;
// date and time
xlWorkSheet.Cells[4, 10] = (string)DateTime.Now.ToShortDateString();
xlWorkSheet.Cells[6, 10] = (string)DateTime.Now.ToShortTimeString();
row = 30;
col = 1;
// left connectors and part number
conCount = nld.n_LeftConnCount;
for (i = 0; i < conCount; i++)
{
xlWorkSheet.Cells[row, col] = "Name: " + nld.ConnDataLeft[i].s_ConnName + " PartNo: " + nld.ConnDataLeft[i].s_ConnPartNumber;
row++;
}
// Right connectors and part number
row = 30;
col = 7;
conCount = nld.n_RightConnCount;
for (i = 0; i < conCount; i++)
{
xlWorkSheet.Cells[row, col] = "Name: " + nld.ConnDataRight[i].s_ConnName + " PartNo: " + nld.ConnDataRight[i].s_ConnPartNumber;
row++;
}
// put down the pin map onNetlist worksheet
xlWorkSheet = xlWorkBook.Worksheets.get_Item(2);
row = 5;
col = 1;
i = 0;
leftPinNum = 0;
int connCount = nld.pinMap.Count;
for(i = 0; i < connCount; i++)
{
xlWorkSheet.Cells[row, col] = (i+1).ToString();
leftPinNum = nld.pinMap[i].pinLeft;
xlWorkSheet.Cells[row, col + 1] = nld.pinMap[i].conLeftName;
xlWorkSheet.Cells[row, col + 2] = nld.pinMap[i].pinLeftName;
xlWorkSheet.Cells[row, col + 4] = nld.pinMap[i].conRightName;
xlWorkSheet.Cells[row, col + 5] = nld.pinMap[i].pinRightName;
row++;
}
// put down the pin map onNetlist worksheet
xlWorkSheet = xlWorkBook.Worksheets.get_Item(3);
row = 5;
col = 1;
i = 0;
leftPinNum = 0;
for (i = 0; i < connCount; i++)
{
xlWorkSheet.Cells[row, col] = (i + 1).ToString();
leftPinNum = nld.pinMap[i].pinLeft;
xlWorkSheet.Cells[row, col + 1] = nld.pinMap[i].conLeftName;
xlWorkSheet.Cells[row, col + 2] = nld.pinMap[i].pinLeftName;
xlWorkSheet.Cells[row, col + 4] = nld.pinMap[i].conRightName;
xlWorkSheet.Cells[row, col + 5] = nld.pinMap[i].pinRightName;
if (facadeIntoNetList.ReturnIfUseShort(i))
{
xlWorkSheet.Cells[row, col + 7] = "True";
}
else
{
xlWorkSheet.Cells[row, col + 9] = "True";
}
row++;
}
// put down the pin map onNetlist worksheet
xlWorkSheet = xlWorkBook.Worksheets.get_Item(4);
row = 5;
col = 1;
i = 0;
leftPinNum = 0;
for (i = 0; i < connCount; i++)
{
xlWorkSheet.Cells[row, col] = (i + 1).ToString();
leftPinNum = nld.pinMap[i].pinLeft;
xlWorkSheet.Cells[row, col + 1] = nld.pinMap[i].conLeftName;
xlWorkSheet.Cells[row, col + 2] = nld.pinMap[i].pinLeftName;
xlWorkSheet.Cells[row, col + 6] = nld.pinMap[i].conRightName;
xlWorkSheet.Cells[row, col + 7] = nld.pinMap[i].pinRightName;
row++;
}

I know the temptation to do this: Those Office COM interfaces are painfully slow. But they also don't support multithreading at all. It's not a C# issue, it is an Excel+COM issue. If you need speed, then write an .xlsx using a 3rd-party library then launch Excel to open the file. That might literally be hundreds of times faster.

Related

Formula not identify cell values

I'm generating an excel using the following code in my ASP.Net MVC Application
var fileName = DateTime.Now.ToString("yyyy-MM-dd--hh-mm-ss") + ".xlsx";
var outputDir = ConfigurationManager.AppSettings["ExcelUploadPath"];
// var fileName = "ExcellData.xlsx";
var file = new FileInfo(outputDir + fileName);
var fDate = JsonConvert.DeserializeObject<DateTime>(fromDate);
var tDate = JsonConvert.DeserializeObject<DateTime>(toDate);
using (var package = new OfficeOpenXml.ExcelPackage(file))
{
// add a new worksheet to the empty workbook
ExcelWorksheet worksheet = package.Workbook.Worksheets.Add("Plan " + DateTime.Now.ToShortDateString());
// --------- Data and styling goes here -------------- //
DataTable dt = planService.GetFlow(fDate, tDate, customerId, ordertypeId, suppliers, items);
if (dt != null)
{
int iCol = 1;
// Add column headings...
for (int i = 9; i < dt.Columns.Count; i++)
{
dt.Columns[i].ColumnName = dt.Columns[i].ColumnName.MultiInsert("/", 1, 3);
}
foreach (DataColumn c in dt.Columns)
{
worksheet.Cells[1, iCol].Value = c.ColumnName;
worksheet.Cells[1, iCol].Style.Fill.PatternType = ExcelFillStyle.Solid;
worksheet.Cells[1, iCol].Style.Font.Bold = true;
worksheet.Cells[1, iCol].Style.Fill.BackgroundColor.SetColor(Color.LightGray);
iCol++;
}
for (int j = 0; j < dt.Rows.Count; j++)
{
for (int k = 0; k < dt.Columns.Count; k++)
{
worksheet.Cells[j + 2, k + 1].Value = dt.Rows[j].ItemArray[k].ToString();
if (int.Parse(dt.Rows[j].ItemArray[7].ToString()) == 6)
{
worksheet.Cells[j + 2, k + 1].Style.Locked = false;
worksheet.Cells[j + 2, k + 1].Style.Fill.PatternType = ExcelFillStyle.Solid;
worksheet.Cells[j + 2, k + 1].Style.Fill.BackgroundColor.SetColor(Color.Cyan);
}
if (int.Parse(dt.Rows[j].ItemArray[7].ToString()) == 7)
{
worksheet.Cells[j + 2, k + 1].Style.Locked = false;
worksheet.Cells[j + 2, k + 1].Style.Fill.PatternType = ExcelFillStyle.Solid;
worksheet.Cells[j + 2, k + 1].Style.Fill.BackgroundColor.SetColor(Color.Magenta);
//worksheet.Cells[j + 2, k + 1].Formula =
if((k+1) > 10){
var addressList = new List<string>();
for (int i = 11; i <= k+1; i++)
{
addressList.Add(worksheet.Cells[((j + 2) -1) , i].Address);
}
var lstAdress = String.Join(",", addressList);
worksheet.Cells[j + 2, k + 1].Formula = "SUM(" + lstAdress + ")";
}
}
if (int.Parse(dt.Rows[j].ItemArray[7].ToString()) == 8)
{
//worksheet.Cells[j + 2, k + 1].Style.Locked = false;
worksheet.Cells[j + 2, k + 1].Style.Fill.PatternType = ExcelFillStyle.Solid;
worksheet.Cells[j + 2, k + 1].Style.Fill.BackgroundColor.SetColor(Color.Gray);
}
}
var colCount = dt.Columns.Count;
// worksheet.Cells[j+2, 8, j+2, colCount- 1].Style.Numberformat.Format = "0.000";
var range = worksheet.Cells[j + 2, 9, j + 2, colCount - 1];
var r = range.ToString();
var decimalValidation = worksheet.DataValidations.AddDecimalValidation(range.ToString());
decimalValidation.ShowErrorMessage = true;
decimalValidation.ErrorStyle = ExcelDataValidationWarningStyle.stop;
decimalValidation.ErrorTitle = "The value you entered is not valid";
decimalValidation.Error = "This cell must be a valid positive number.";
decimalValidation.Operator = ExcelDataValidationOperator.greaterThanOrEqual;
decimalValidation.Formula.Value = 0D;
}
worksheet.Cells[worksheet.Dimension.Address].AutoFitColumns();
worksheet.Column(1).Hidden = true;
worksheet.Column(2).Hidden = true;
worksheet.Column(3).Hidden = true;
worksheet.Column(4).Hidden = true;
worksheet.Column(5).Hidden = true;
worksheet.Column(8).Hidden = true;
worksheet.Column(9).Hidden = true;
worksheet.Column(10).Hidden = true;
worksheet.Protection.IsProtected = true;
// save our new workbook and we are done!
worksheet.Calculate();
package.Save();
return Json(fileName, JsonRequestBehavior.AllowGet);
}
else
{
return Json("NoData", JsonRequestBehavior.AllowGet);
}
}
return Json("", JsonRequestBehavior.AllowGet);
Here I'm setting my formula with comma separated cell names eg:
SUM(A1,A2,A3.. etc)
The excel file is generating correctly. But the problem is the formula calculation is not happen when I open my excel file.
The formula works when I manually change a cell value in Column Type Agreed Flow.
And it can only identify values of manually edited cells.
How can I resolve this?
Formula recalculation is both an Excel and a workbook setting.
You could set it with at the workbook level with
workbook.CalcMode = ExcelCalcMode.Automatic;
If the user has set it to manual though, the formulas won't be recalculated.
If you want to ensure the saved values are correct you can force the calculation by calling
worksheet.Calculate();
You can also calculate the formulas at the workbook or range level, eg :
worksheet.Cells[j + 2, k + 1].Calculate();
or
package.Workbook.Calculate();
This is explained in the documentation. Keep in mind that EPPlus doesn't contain Excel's formula engine. It uses its own engine to parse and calculate formulas. Some things aren't supported
It worked when I change my formula as follows..
var addressList = new List<string>();
for (int i = 11; i <= k+1; i++)
{
addressList.Add(worksheet.Cells[((j + 2) -1) , i].Address);
}
var lstAdress = String.Join("+", addressList);
worksheet.Cells[j + 2, k + 1].Formula = "(" + lstAdress + ")";
I think there is an issue in my excel sheet when I use the SUM function So I write the formula without using it. Then it worked
(A1+B1+C1+D1+ ..... etc)

Data successfully export to Excel from gridview except ID column

Here is my code...
Microsoft.Office.Interop.Excel.Application Excel = new Microsoft.Office.Interop.Excel.Application();
Workbook wb = Excel.Workbooks.Add(XlSheetType.xlWorksheet);
Worksheet ws = (Worksheet)Excel.ActiveSheet;
Excel.Visible = true;
ws.Cells[1, 1] = "ID";
ws.Cells[1, 2] = "Name";
ws.Cells[1, 3] = "Detail";
ws.Cells[1, 4] = "Category";
ws.Cells[1, 5] = "Brand";
ws.Cells[1, 6] = "In Stock";
ws.Cells[1, 7] = "Price";
ws.Cells[1, 8] = "Total";
for (int i = 2; i <= dgDetail.Rows.Count; i++)
{
for (int j = 2; j <= 8; j++)
{
ws.Cells[i, j] = dgDetail.Rows[i - 2].Cells[j - 1].Value;
}
}
Excel has 1,1 based arrays, the first cell is 1,1, so you did it right when you put "ID" to this cell. Your gridview looks zero-based.
1) You start to export dgDetail from row 0, which I think is right, but you skip one last row - you can rewrite your code a little bit to see it:
for (int i = 0; i <= dgDetail.Rows.Count-2; i++) {
dgDetail.Rows[i]; //pseudocode
}
Maybe it's how you planned it, but you don't export row dgDetail.Row.Count - 1
2)
for (int j = 2; j <= 8; j++) {
ws.Cells[i, j] = dgDetail.Rows[i - 2].Cells[j - 1].Value;
}
Here you need to change the first value of j to 1, because you start from second column otherwise:
ws.Cells[i, 2] = dgDetail.Rows[i - 2].Cells[2 - 1].Value;
I guess you are just confused by excel 1,1 based arrays a little bit,
Also, if you have big exports, it's better to write the whole array in one operation:
Range range = worksheet.Range[worksheet.Cells[1, 1], worksheet.Cells[2, 2]];
range.Value = new object[2, 2] { { 1, 2 }, { 3, 4 } };

Exporting DataGridView data into a new excel file - How to include column names?

I am trying this but nothing get Column name or header in excel.
for (i = 1; i < dataGridView2.Columns.Count + 1; i++)
{
xlWorkSheet.Cells[1, i] = dataGridView2.Columns[i - 1].Name;
}
for (i = 0; i <= dataGridView2.RowCount - 1; i++)
{
for (j = 0; j <= dataGridView2.ColumnCount - 1; j++)
{
DataGridViewCell cell = dataGridView2[j, i];
xlWorkSheet.Cells[i + 1, j + 1] = cell.Value;
}
}
Use the HeaderText instead of the ColumnName. This should give you the text which is displayed on the DataGridView.
xlWorkSheet.Cells[1, i] = dataGridView2.Columns[i - 1].HeaderText;

Excel.Range Copy() works very slowly

I'm building a report in which try to copy the previous row cell styles in the following
for (int i = 0; i < DataSource.Length; i++)
{
int rowNumber = i + s;
Excel.Range RngToCopy = ObjWorkSheet.get_Range("A" + rowNumber.ToString(), "K" + rowNumber.ToString());
Excel.Range r = ObjWorkSheet.get_Range("A" + (rowNumber + 1).ToString(), "K" + (rowNumber + 1).ToString());
RngToCopy.Copy(Type.Missing);
r.Insert(Excel.XlInsertShiftDirection.xlShiftDown);
r.PasteSpecial(Excel.XlPasteType.xlPasteFormats,
Excel.XlPasteSpecialOperation.xlPasteSpecialOperationNone, false, false);
ObjWorkSheet.Cells[rowNumber, 1] = i + 1;
ObjWorkSheet.Cells[rowNumber, 2] = DataSource[i].TerminalName;
ObjWorkSheet.Cells[rowNumber, 3] = DataSource[i].Type;
ObjWorkSheet.Cells[rowNumber, 4] = DataSource[i].Requisite;
}
everything works but very long
How can I speed this up?
I think you manually copy you will be improved.
Object[] origData = origRange.Value2;
destRange.Value2 = origData;
That should be WAY faster.

exporting datagridview data to excel [excel file should read only]

I want to export data from datagrid to excel file with below code I am exporting the data to excel but Iwant excel file of readonly which I am not getting with below code:
Microsoft.Office.Interop.Excel._Application app = new Microsoft.Office.Interop.Excel.Application();
Microsoft.Office.Interop.Excel._Workbook workbook = app.Workbooks.Add(Type.Missing);
Microsoft.Office.Interop.Excel._Worksheet worksheet = null;
app.Visible = true;
worksheet = workbook.ActiveSheet;
for (int i = 1; i < dg1.Columns.Count + 1; i++)
{
worksheet.Cells[1, i] = dg1.Columns[i - 1].HeaderText;
}
for (int i = 0; i < dg1.Rows.Count - 1; i++)
{
for (int j = 0; j < dg1.Columns.Count; j++)
{
if (dg1.Rows[i].Cells[j].Value != null)
{
worksheet.Cells[i + 2, j + 1] = dg1.Rows[i].Cells[j].Value.ToString();
}
else
{
worksheet.Cells[i + 2, j + 1] = "";
}
}
worksheet.Columns.AutoFit();
Microsoft.Office.Interop.Excel._Application app = new Microsoft.Office.Interop.Excel.Application();
Microsoft.Office.Interop.Excel._Workbook workbook = app.Workbooks.Add(Missing.Value);
Microsoft.Office.Interop.Excel._Worksheet worksheet = workbook.ActiveSheet;
worksheet = workbook.ActiveSheet;
//app.Visible = true;
//getting all the columns from datagrid
for (int i = 1; i < dg1.Columns.Count + 1; i++)
{
worksheet.Cells[1, i] = dg1.Columns[i - 1].HeaderText;
}
//getting row collection
for (int i = 0; i < dg1.Rows.Count - 1; i++)
{
for (int j = 0; j < dg1.Columns.Count; j++)
{
if (dg1.Rows[i].Cells[j].Value != null)
{
worksheet.Cells[i + 2, j + 1] = dg1.Rows[i].Cells[j].Value.ToString();
}
else
{
worksheet.Cells[i + 2, j + 1] = "";
}
worksheet.Columns.AutoFit();
}
}
//to make the excel sheet readonly
((Excel.Worksheet)app.ActiveWorkbook.Sheets[1]).EnableSelection = Excel.XlEnableSelection.xlUnlockedCells;
((Excel.Worksheet)app.ActiveWorkbook.Sheets[1]).Protect(Password: protectionpasswprd, AllowFormattingCells: false);
app.ActiveWorkbook.SaveCopyAs("D:\\a.xls");
app.ActiveWorkbook.Saved = true;
app.Quit();

Categories

Resources