How convert stream excel file to datatable C#? - asp.net

I use Epplus to reading xlsx files from stream.
It has a bug , it cant read some columns in my workbook.How can read xlsx files from stream to datatable without epplus ?
my older code:
public static DataSet ReadExcelFile(Stream stream)
{
try
{
//2. Reading from a OpenXml Excel file (2007 format; *.xlsx)
IExcelDataReader excelReader =
ExcelReaderFactory.CreateOpenXmlReader(stream);
//...
DataSet result = excelReader.AsDataSet();
return result;
}
catch (Exception x)
{
throw x;
}
}
I didnt report it, but i tried so much combinations.If there are empty columns in worksheet ,epplus reader cant read correctly column values.

"It has a bug , it cant read some columns in my workbook"
Can you describe the bug, have you reported it or is it already known, what version are you using?
Here's a simple approach to load an excel file into a DataTable with EPPlus.
public static DataTable getDataTableFromExcel(string path)
{
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();
bool hasHeader = true; // adjust it accordingly( i've mentioned that this is a simple approach)
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 (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;
}
}

This is way past, however it could still help someone.
Apparently some columns in my worksheet were merged, so for example, if columns A and B are merged it only recognizes column A as the one with the value, and so it returns column B as empty, when i call on that particular cell's value(B). To get past this, make sure you know which cells are merged and then grab only the first one and regard the rest of the merged cells as null

Related

Error message "entries cannot be opened multiple times in update mode." in Spreadsheet Lite SaveAs function

Upon execution of the dBWorksheet.SaveAs(xlsFileSpec), in the code below, I am seeing an exception:
"entries cannot be opened multiple times in update mode."
SLDocument dBWorksheet = new SLDocument();
TimeSpan interval = new TimeSpan(0, 0, 2);
dBWorksheet.SetCellValue(2, 1, "Hour");
dBWorksheet.SetCellValue(3, 1, "Time");
int Row = 3;
// Create the hour and time of day columns.
for(TimeSpan dBTime = new TimeSpan(0, 0, 0); dBTime.TotalHours < 24; dBTime = dBTime.Add(interval))
{
dBWorksheet.SetCellValue(Row, 1, dBTime.Hours);
dBWorksheet.SetCellValue(Row, 2, dBTime.ToString());
Row++;
}
// Save the new worksheet.
dBWorksheet.SaveAs(xlsFileSpec);
I had this error when I opened (by SpreadsheetLight) .xlsx file that was saved (by LibreOffice Calc) in "Excel 2007-365" format and then I tried use a SaveAs function.
When I save (by LibreOffice Calc) the .xlsx file as "Office Open XML" then I can Open and SaveAs (by SpreadsheetLight) a .xlsx file without problems.
Here's how I solved it.
Downloaded the source code for SpreadsheetLight (version 3.5).
http://spreadsheetlight.com/downloads/SpreadsheetLight3.5.zip
Created a .NET Core library project with the name "SpreadsheetLight" and added necessary NuGet packages (DocumentFormat.OpenXML and System.Drawing.Common) to it. Copied and pasted all the downloaded source code files in this project.
Added the project "SpreadsheetLight" to my solution and referenced it in one of the existing projects.
In "SLDocument.cs" file, make the following changes in the method "LoadDocumentProperties()" so that the code looks like the following:
// XDocument xdoc = XDocument.Load(XmlReader.Create(xl.CoreFilePropertiesPart.GetStream()));
Stream stream = xl.CoreFilePropertiesPart.GetStream();
XDocument xdoc = XDocument.Load(XmlReader.Create(stream));
foreach (XElement xelem in xdoc.Descendants())
{
// Code omitted.
}
stream.Close();
Build your solution and test it.
Roll-back from .NET Core 3.0 to .NET Framework 4.7.x
This is obviously not the most desirable solution.
However, the only solution that I have found is to roll-back the application from .NET Core 3.0 and SpreadsheetLight.Core to .NET Framework 4.7.x and SpreadsheetLight.
The code posted in the question above runs without modification.
I believe this has to do with a memory leak that was fixed in System.IO.Packaging in .NET Core 3.0. This will require further investigation and probably a fix to SpreadsheetLight.Core.
A bit late to the party but just bumped in to this problem.
I solved this by create a new SLDocument and copied cell by cell from the old SLDocument. Might not work 100%, but it has covered my reports so far.
Code
using (var file = new SLDocument())
{
file.CopyFromTemplate(Path.Combine("ReportTemplates\\Tackningsbidrag_budget.xlsx"), maxCols: 20, maxRows: 10);
// code
using (var ms = new MemoryStream())
{
file.SaveAs(ms);
}
}
Extension method:
public static void CopyFromTemplate(this SLDocument file, string pathToOrgFile, int? maxCols = null, int? maxRows = null)
{
using (var orgFile = new SLDocument(pathToOrgFile))
{
var page = orgFile.GetPageSettings();
file.SetPageSettings(page);
foreach (var cell in orgFile.GetWorksheetMergeCells())
{
file.MergeWorksheetCells(cell.StartRowIndex, cell.StartColumnIndex, cell.EndRowIndex, cell.EndColumnIndex);
}
var stats = orgFile.GetWorksheetStatistics();
var endCol = stats.EndColumnIndex;
if (maxCols.HasValue && maxCols < endCol)
{
endCol = maxCols.Value;
}
var endRow = stats.EndRowIndex;
if (maxRows.HasValue && maxRows < endRow)
{
endRow = maxRows.Value;
}
for (int col = stats.StartColumnIndex; col <= endCol; col++)
{
file.SetColumnStyle(col, orgFile.GetColumnStyle(col));
file.SetColumnWidth(col, orgFile.GetColumnWidth(col));
}
for (int row = stats.StartRowIndex; row <= endRow; row++)
{
file.SetRowStyle(row, orgFile.GetRowStyle(row));
file.SetRowHeight(row, orgFile.GetRowHeight(row));
}
for (int row = stats.StartRowIndex; row <= endRow; row++)
{
for (int col = stats.StartColumnIndex; col <= endCol; col++)
{
var formula = orgFile.GetCellFormula(row, col);
var stringValue = orgFile.GetCellValueAsString(row, col);
file.SetCellValue(row, col, !string.IsNullOrWhiteSpace(formula) ? ("=" + formula) : stringValue);
file.SetCellStyle(row, col, orgFile.GetCellStyle(row, col));
}
}
}
}

asp.net open xml to download .xlsx file

I'm trying to supply a .xlsx file from a grid, I think most of the hard work is done. I'm picking up a template file and filling it with data.
I' getting an error 'file not found' on Response.WriteFile.
by the looks of the example (linked below) this should just be the intended file name, but I imagine this needs to be a path to the file?, so do I need to save my 'myDoc' object to the server and then provide the path in the Reponse.WriteFile.
It doesn't seem like that is what is meant by the example.
the code i'm using is a modified version of : http://technet.weblineindia.com/web/export-data-to-excel-using-openxml-sdk/
due to using sharepoint to store the template file I just had to create a filestream rather than supply the URL to the file.
here is my code:
// Create cell reference array
string[] CellReferenceArray = new string[] { "A", "B", "C", "D", "E" };
//Open your saved excel file that you have created using template file.
using (SpreadsheetDocument myDoc = SpreadsheetDocument.Open(file.OpenBinaryStream(), true))
{
// Create reference of main Workbook part, which contains all reference.
WorkbookPart objworkbook = myDoc.WorkbookPart;
// Create style sheet object that will be used for applying styling.
Stylesheet objstyleSheet = objworkbook.WorkbookStylesPart.Stylesheet;
// pick up first worksheet
WorksheetPart objworksheet = objworkbook.WorksheetParts.First();
// will be used in end while creating sheet data
string objorigninalSheetId = objworkbook.GetIdOfPart(objworksheet);
WorksheetPart objreplacementPart = objworkbook.AddNewPart<WorksheetPart>();
string objreplacementPartId = objworkbook.GetIdOfPart(objreplacementPart);
// Create object reader to read from excel file.
OpenXmlReader objreader = OpenXmlReader.Create(objworksheet);
// create writer object to write in excel sheet.
OpenXmlWriter objOpenXmwriter = OpenXmlWriter.Create(objreplacementPart);
int i = 1;
Row r = new Row();
Cell c = new Cell();
Columns col1 = new Columns();
UInt32 index = 0;
while (objreader.Read())
{
if (objreader.ElementType == typeof(SheetData))
{
if (objreader.IsEndElement)
continue;
objOpenXmwriter.WriteStartElement(new SheetData());
objOpenXmwriter.WriteStartElement(r);
// Loop to insert header
foreach (DataColumn colHead in YoutdTName.Columns)
{
c = new Cell
{
DataType = CellValues.String,
CellReference = CellReferenceArray[i] + Convert.ToString(index)
};
CellValue v1 = new CellValue(colHead.ColumnName.ToString());
c.Append(v1);
objOpenXmwriter.WriteElement(c);
i += 1;
}
objOpenXmwriter.WriteEndElement();
index += 1;
//Loop to insert datatable row in excel
foreach (DataRow dr in YoutdTName.Rows)
{
objOpenXmwriter.WriteStartElement(r);
i = 1;
foreach (DataColumn col in YoutdTName.Columns)
{
c = new Cell
{
DataType = CellValues.String,
CellReference = CellReferenceArray[i] + Convert.ToString(index)
};
CellValue v1 = new CellValue(dr[col].ToString());
c.AppendChild(v1);
objOpenXmwriter.WriteElement(c);
i += 1;
}
objOpenXmwriter.WriteEndElement();
index += 1;
}
objOpenXmwriter.WriteEndElement();
}
}
//close all objects
objreader.Close();
objOpenXmwriter.Close();
Sheet sheet = objworkbook.Workbook.Descendants<Sheet>().Where(s => s.Id.Value.Equals(objorigninalSheetId)).First();
sheet.Id.Value = objreplacementPartId;
objworkbook.DeletePart(objworksheet);
}
Response.AddHeader("Content-Disposition", "inline;filename=YourExcelfileName.xlxs");
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.WriteFile("YourExcelfileName.xlxs");
Response.Flush();
Response.End();
}
Use HttpResponse.BinaryWrite instead and take the underlying stream from your SpreadsheetDocument-instance .
http://msdn.microsoft.com/en-us/library/system.web.httpresponse.binarywrite(v=vs.110).aspx

Save SAP RFC structure to MSSQL table?

Recently i am working on a project that required to fetch the data from SAP table to MICROSOFT SQL table ,I have completed the connection part and i also pulled the SAP structure values into ASP.NET.
My question is how to save the structure into Sql table ?
here is my code ;
RfcDestination mydestination = RfcDestinationManager.GetDestination("rfcecp");
RfcRepository myrepository = mydestination.Repository;
IRfcFunction EHSIMSFM = myrepository.CreateFunction("ZEHSIMS");
EHSIMSFM.Invoke(mydestination);
IRfcTable positable = EHSIMSFM.GetTable("POSITIONTAB1");
if (positable.RowCount > 0)"THIS IS THE SAP STRUCTURE WITH ROW COUNT '300'."
{
posid.Text = "working";
}
else
{
posid.Text = "notworking";
}
}
catch (Exception ej)
{
posid.Text = ej.Message;
}
You can gather all the information from a returning IRfcTable via NCo 3 SAP Connector.
Here is the way to traverse each row (IRfcStructure) from the IRfcTable object..
foreach (IRfcStructure row in rfcTable)
{
for (int element = 0; element < rfcTable.ElementCount; element++)
{
RfcElementMetadata metadata = rfcTable.GetElementMetadata(element);
// row.GetString(metadata.Name); // get value from row's field
}
// You have your data from a row here..
}
Example: Here is the code which converts IRfcTable into DataTable.
public static DataTable CreateDataTable(IRfcTable rfcTable)
{
var dataTable = new DataTable();
for (int element = 0; element < rfcTable.ElementCount; element++)
{
RfcElementMetadata metadata = rfcTable.GetElementMetadata(element);
dataTable.Columns.Add(metadata.Name);
}
foreach (IRfcStructure row in rfcTable)
{
DataRow newRow = dataTable.NewRow();
for (int element = 0; element < rfcTable.ElementCount; element++)
{
RfcElementMetadata metadata = rfcTable.GetElementMetadata(element);
newRow[metadata.Name] = row.GetString(metadata.Name);
}
dataTable.Rows.Add(newRow);
}
return dataTable;
}
Similarly, you can add your data into MSSQL DB.

OpenXML Excel File Parsing

I have an excel document that is supposed to be parsed using the following block of code:
var handler = new ExcelHandler(byteStream);
var currentExcelDocument = handler.CurrentDocument;
//now if the document is null
if (currentExcelDocument == null)
{
throw new Exception("Excel file handle missing");
}
var wokrbookpart = currentExcelDocument.WorkbookPart;
//get a list of all the sheets
string text = String.Empty;
var sheets = wokrbookpart.Workbook.Descendants<Sheet>().ToList();
foreach (var sh in sheets)
{
//now we have the sheets but the data must be in the first sheet
var themainsheet = sh;
if (themainsheet == null)
throw new ArgumentException("sheetName");
//now we need a reference to the worksheetpart
var worksheetpart = (WorksheetPart)wokrbookpart.GetPartById(themainsheet.Id);
SheetData data = worksheetpart.Worksheet.Elements<SheetData>().First();
//now we have a reference to the cells that contain data we start passing the data
foreach (Row r in data.Elements<Row>())
{
//we get the cells in the row
var cells = r.Elements<Cell>().ToList();
foreach (var i in cells)
{
text += i.CellValue.InnerText;
}
}
}
return text;
the excel file is uploaded via a fileupload control in asp.net and the stream is passed into the above block of code.
All is well though but then when i display the resulting text in the label, I see that the
result of reading the cell values is displayed as a sequence of numbers even though i am hindred percent sure that there are no numbers in the data
Please what could i be doing wrong??

ASP.NET Backgroundworkers for spreadsheet creation: multiple ones interfering with each other?

I am writing an ASP.NET application in which i need to create multiple excel reports. the report creation is pretty time-consuming (up to ten seconds for each) so i am using backgroundworkers to create them simultaneously.
My code looks a bit like this:
if (condition1)
{
excel_file_name = "TRANSFER";
BackgroundWorker worker_t = new BackgroundWorker();
worker_t.DoWork += new DoWorkEventHandler(DoWork);
worker_t.WorkerReportsProgress = false;
worker_t.WorkerSupportsCancellation = true;
worker_t.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(WorkerCompleted);
worker_t.RunWorkerAsync(excel_file_name);
}
if (Condition2)
{
excel_file_name = "NEFT";
BackgroundWorker worker_n = new BackgroundWorker();
worker_n.DoWork += new DoWorkEventHandler(DoWork);
worker_n.WorkerReportsProgress = false;
worker_n.WorkerSupportsCancellation = true;
worker_n.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(WorkerCompleted);
worker_n.RunWorkerAsync(excel_file_name);
}
there are more conditions but i haven't written them, since they are all similar. the only difference is the Excel_File_Name
the DoWork even then calls a class to create the excel files with the given name.
When condition1 and condition2 are both true, Here is the issue:
1. if i run this slowly using breakpoints during debugging, both files (TRANSFER and NEFT) are created.
2. if, however, i run it without breakpoints like a normal application, only the last file (NEFT in this example) is created.
What can be the issue?
Thanks
PS: For further information, here is the important code from the class that creates the excel file:
private static string placeDataInTemplate(string destFilePath, DataRow dr, bool isCoverLetter)
{
int loop = 0;
ExcelNamespace.Application excelApplication = new ExcelNamespace.Application();
ExcelNamespace.Workbook workbook = excelApplication.Workbooks.Open(destFilePath, 0, false, 5,
"", "", true, ExcelNamespace.XlPlatform.xlWindows, "\t", false, false, 0, true, true, false);
ExcelNamespace.Worksheet workSheet = (ExcelNamespace.Worksheet)workbook.Sheets[sheet_no];
try
{
string value;
string replicate;
string replicate_end;
// get data for Place Holders
sDataTable dtPlaceHolderData = getPlaceHolderData(dr);
//make Display Alerts False
excelApplication.DisplayAlerts = false;
if (dtPlaceHolderData != null && dtPlaceHolderData.Rows.Count > 0)
{
int rowCntDt = 0; //Which row will be used for data?
int i = 1;
Excel.Range Find = (ExcelNamespace.Range)workSheet.Cells.Find("#",
(ExcelNamespace.Range)workSheet.Cells[1, 1],
Excel.XlFindLookIn.xlValues,
Excel.XlLookAt.xlPart,
Excel.XlSearchOrder.xlByRows,
Excel.XlSearchDirection.xlNext,
false,
false,
Missing.Value);
while (Find != null && loop <= 200)
{
loop++;
value = Find.Value2.ToString();
if (condition)
//VERY long if...else if
}
string approveDirPath = destFilePath.Replace(Path.GetFileName(destFilePath), string.Empty);
workbook.Close(true, destFilePath, Type.Missing);
excelApplication.Quit();
string filepath = destFilePath.Split('-')[0];
string approval_id = dr[0].ToString();
return destFilePath;
}
return string.Empty;
}
catch (Exception ex)
{
//do something
}
finally
{
//release resources
}
NOTE: I have removed a lot of needless code. I can paste it if needed. Thank you
Most likely cause is some shared state between two threads - shared state may include excel application and workbooks. So you need to inspect your code for the same.
On the side note, instead of using Excel Automation to generate excel files, you may consider using some in-process library which would be perhaps more scalable and void of such issues. Have a look at one such free basic library at code project

Resources