Closed XML Reading in a table Through Empty Rows - c#

I have a method that reads from a spreadsheet to create an Auction for my application. I am getting the exception below, and I was wondering why the range of my sheet was set to that and then how to change the range so that I can read the rest of my excel file.
public ActionResult Upload(UploadFile UploadFile)
{
if (ModelState.IsValid)
{
if (UploadFile.ExcelFile.ContentLength > 0)
{
if (UploadFile.ExcelFile.FileName.EndsWith(".xlsx") || UploadFile.ExcelFile.FileName.EndsWith(".xls"))
{
XLWorkbook wb;
//incase if the file is corrupt
try
{
wb = new XLWorkbook(UploadFile.ExcelFile.InputStream);
}
catch (Exception ex)
{
ModelState.AddModelError(String.Empty, $"Check your file. {ex.Message}");
return View();
}
IXLWorksheet ws = null;
try//incase if the sheet you are looking for is not found
{
ws = wb.Worksheet("sheet1");
}
catch
{
ModelState.AddModelError(String.Empty, "Sheet not found");
return View();
}
var firstRowUsed = ws.FirstRowUsed();
var auctionRow = firstRowUsed.RowUsed().RowBelow();
//create auction
string auctionName = auctionRow.Cell(1).Value.ToString();
DateTimeOffset startDate = DateTimeOffset.Parse(auctionRow.Cell(2).Value.ToString());
DateTimeOffset endDate = DateTimeOffset.Parse(auctionRow.Cell(3).Value.ToString());
string folderName = auctionRow.Cell(4).Value.ToString();
Models.Auction auction = new Models.Auction(auctionName, startDate, endDate, folderName);
db.Auctions.Add(auction);
//find the next table
var nextRow = auctionRow.RowBelow();
while (nextRow.IsEmpty())
{
nextRow = nextRow.RowBelow();
}
const int catNameCol = 1;
var firstCatRow = nextRow.RowUsed();
var catRow = firstCatRow.RowBelow();
//get categories from ws table and add to the auction
while (!catRow.Cell(catNameCol).IsEmpty())
{
string catName = catRow.Cell(1).Value.ToString();
int seqNo = Convert.ToInt32(catRow.Cell(2).Value.ToString());
string fileName = catRow.Cell(3).Value.ToString();
Cat cat = new Cat(auction.AuctionId, catName, seqNo, fileName);
auction.Cats.Add(cat);
catRow = catRow.RowBelow();
}
var findNextRow = catRow.RowBelow();
while (findNextRow.IsEmpty())
{
findNextRow = findNextRow.RowBelow();
}
const int itemNameCol = 1;
var itemRow = findNextRow.RowUsed().RowBelow();
while (!itemRow.Cell(itemNameCol).IsEmpty())
{
string itemName = itemRow.Cell(1).Value.ToString();
string itemDesc = itemRow.Cell(2).Value.ToString();
string catName = itemRow.Cell(3).Value.ToString();
string modelNo = itemRow.Cell(4).Value.ToString();
decimal retailValue = Convert.ToDecimal(itemRow.Cell(5).Value.ToString());
string fileName = itemRow.Cell(6).Value.ToString();
decimal initialBid = Convert.ToDecimal(itemRow.Cell(7).Value.ToString());
decimal increment = Convert.ToDecimal(itemRow.Cell(8).Value.ToString());
Cat itemCat = null;
foreach (var cat in auction.Cats)
{
if (catName == cat.CatName.Trim())
{
itemCat = cat;
}
}
Item item = new Item(itemName, itemDesc, modelNo, retailValue, fileName, startDate, endDate, initialBid, increment, null, null, null, itemCat);
itemCat.Items.Add(item);
itemRow = itemRow.RowBelow();
}
}
else
{
ModelState.AddModelError(String.Empty, "Only .xlsx and .xls files are allowed");
return View();
}
}
else
{
ModelState.AddModelError(String.Empty, "Not a valid file");
return View();
}
}
db.SaveChanges();
return View();
}
Exception:
Format of Excel Doc:
I am really new to this so let me know if there is some syntax to accomplish what I am trying to and fix this exception. Also let me know if there are anything that sticks out that I can change/improve. Thank you kindly

nextRow is a truncated row "C15: F15", and the data you are looking for is greater than this range. I think that there is also a ClosedXML error here.
In your case, it helps to take the entire row of the worksheet and then get used cells.
Try next:
var firstCatRow = nextRow.WorksheetRow().RowUsed();

Related

Change for loop to forEach

I am working on a inventory updating program in vs2022 which I am using Gembox Spreadsheet for, It is working right now, but the boss man would like me to try to convert my for loop to a for each loop. The loop is what I am using to update my file.
Current For Loop
public void updateFile(string filename, int range, ItemLine selectedItem, decimal outValue)
{
var fullPath = $".\\Backend Files\\{filename}";
SpreadsheetInfo.SetLicense("FREE -LIMITED-KEY");
var workbook = ExcelFile.Load(fullPath, new CsvLoadOptions(CsvType.CommaDelimited));
var worksheet = workbook.Worksheets[0];
//foreach (var row in worksheet.Rows.Skip(1))
//{
// foreach (var cell in row.AllocatedCells)
// {
// string updateValue =
// if(cell.Value == int.TryParse((int)outValue, out int updateValue))
// {
// }
// }
//}
for (int i = 1; i <= range; i++)
{
var plan = worksheet.Rows[i].Cells[0].Value;
var desc = worksheet.Rows[i].Cells[1].Value;
var csvDescription = plan + " - " + desc;
var platedescription = plan + " - ";
if (selectedItem.ComboDescription == csvDescription)
{
worksheet.Rows[i].Cells[2].Value = selectedItem.Quantity;
}
if (selectedItem.plateDescription == platedescription)
{
worksheet.Rows[i].Cells[1].Value = selectedItem.Quantity;
}
}
workbook.Save(fullPath, new CsvSaveOptions(CsvType.CommaDelimited));
}
I beleive the forEach would look similar to this
private static List<ItemLine> ReadFile(string fileName, string defaultValueDescription)
{
string path = $".\\Backend Files\\{fileName}";
SpreadsheetInfo.SetLicense("FREE -LIMITED-KEY");
var workbook = ExcelFile.Load(path, new CsvLoadOptions(CsvType.CommaDelimited));
var worksheet = workbook.Worksheets[0];
var items = new List<ItemLine>();
items.Add(new ItemLine { Description = defaultValueDescription, Quantity = 0 });
foreach (var row in worksheet.Rows.Skip(1))
{
var cells = row.AllocatedCells;
var il = new ItemLine();
if (cells.Count == 2)
{
il.Description = cells[0].Value?.ToString() ?? "Description Not Found";
il.Quantity = cells[1].IntValue;
}
else if (cells.Count >= 3)
{
il.Plan = cells[0].Value?.ToString() ?? "Plan Not Found";
il.Description = cells[1].Value?.ToString() ?? "Description Not Found";
il.Quantity = cells[2].IntValue;
}
items.Add(il);
}
return items;
}

How to export list to excel without Microsoft.Office.Interop.Excel.dll

I have list of foo as the below. How to export it to excel without Microsoft.Office.Interop.Excel.dll
public class Foo
{
public DateTime Column1 { get; set; }
public string Column2 { get; set; }
public decimal Column3 { get; set; }
public bool Column4 { get; set; }
}
You can do this with ClosedXML library( https://closedxml.codeplex.com/ )
I just write a simple example to show you how you can name the file, the worksheet and select cells:
var workbook = new XLWorkbook();
workbook.AddWorksheet("sheetName");
var ws = workbook.Worksheet("sheetName");
int row = 1;
foreach (Foo f in fooList)
{
string rowString = row.ToString();
ws.Cell("A" + rowString).Value = f.Column1;
ws.Cell("B" + rowString).Value = f.Column2;
ws.Cell("C" + rowString).Value = f.Column3;
ws.Cell("D" + rowString).Value = f.Column4;
row++;
}
workbook.SaveAs("yourExcel.xlsx");
If you prefer you can create a System.Data.DataSet or a System.Data.DataTable with all data and then just add it as a workseet with workbook.AddWorksheet(yourDataset) or workbook.AddWorksheet(yourDataTable);
Super easy way to export your list to excel using c#
How to install ClosedXML with NuGet Packager Manager Console: PM> Get-Project [ProjectName] | Install-Package ClosedXML
using (var conn = new DB.UpdatesEntities())
{
var stories = (from a in conn.Subscribers
orderby a.DT descending
select a).Take(100).ToList();
var ShowHeader = true;
PropertyInfo[] properties = stories.First().GetType().GetProperties();
List<string> headerNames = properties.Select(prop => prop.Name).ToList();
var wb = new XLWorkbook();
var ws = wb.Worksheets.Add("Subscribers");
if (ShowHeader)
{
for (int i = 0; i < headerNames.Count; i++)
ws.Cell(1, i + 1).Value = headerNames[i];
ws.Cell(2, 1).InsertData(stories);
}
else
{
ws.Cell(1, 1).InsertData(stories);
}
wb.SaveAs(#"C:\Testing\yourExcel.xlsx");
}
For Desktop app
public static void WriteListToExcel<T>(List<T> list, string fulllPath)
{
try
{
List<string> result = new List<string>();
result.Add(String.Join(String.Empty, typeof(T).GetProperties().Select(i => String.Format("{0}\t", i.Name)))); // Headers
result.AddRange(list.Select(i => String.Join("\t", i.GetType().GetProperties().Select(t => t.GetValue(i, null))))); // Lines
File.WriteAllLines(fulllPath, result);
}
catch (Exception e)
{
// Error do what you want....
}
}
For Web app
public static void WriteListToExcel<T>(HttpResponseBase Response, List<T> list, string fileName)
{
try
{
Response.Clear();
Response.AddHeader("content-disposition", String.Format("attachment;filename={0}.xls", fileName));
Response.Charset = String.Empty;
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.ContentType = "application/vnd.ms-excel";
List<string> result = new List<string>();
result.Add(String.Format("{0}\n", String.Join(String.Empty, typeof(T).GetProperties().Select(i => String.Format("{0}\t", i.Name))))); // Headers
result.AddRange(list.Select(i => String.Format("{0}\n",String.Join("\t", i.GetType().GetProperties().Select(t => t.GetValue(i, null)))))); // Lines
result.ForEach(i => Response.Write(i));
Response.Flush();
Response.End();
}
catch (Exception e)
{
// Error..
}
}

MVC how to read only user specific records from database uploaded with a relational foreign key

I have a scenario in MVC where I allow users to uplaod a excel file, the data gets written to a database which has a relational table(foregin key) to another table. Table A has all the records with foregih key to Table B. Users can simultanealy uplaod records to the database, how can I get only the recors that specific user ihas uplaoded to database, I have tried the below code but it return all the recorsd in the database. PLease help.
[HttpPost]
public ActionResult Upload(HttpPostedFileBase file)
{
List<CleanSupplierClaim> supplierClaimsData = new List<CleanSupplierClaim>();
try
{
if (file.ContentLength > 0)
{
string path = file.FileName;
Excel.Application application = new Excel.Application();
Excel.Workbook workbook = application.Workbooks.Open(path);
Excel.Worksheet worksheet = workbook.ActiveSheet;
Excel.Range range = worksheet.UsedRange;
for (int row = 5; row < range.Rows.Count; row++)
{
CleanSupplierClaim supplierList = new CleanSupplierClaim();
supplierList.Action = ((Excel.Range)range.Cells[row, 1]).Text;
supplierList.Line_Number = ((Excel.Range)range.Cells[row, 2]).Text;
supplierList.Total_Claim = Decimal.Parse(((Excel.Range)range.Cells[row, 3]).Text);
supplierList.Currency = ((Excel.Range)range.Cells[row, 4]).Text;
supplierList.ClaimReference = ((Excel.Range)range.Cells[row, 5]).Text;
if (String.IsNullOrEmpty(supplierList.Action) && String.IsNullOrEmpty(supplierList.Line_Number) && supplierList.Total_Claim == null && String.IsNullOrEmpty(supplierList.Currency))
{
break;
}
supplierClaimsData.Add(supplierList);
}
//Here I write to the foregin key table and then read thag ID to save it to all my records as a relationship id
var cleanGPSessionObject = new CleanSupplierClaimsUploadSession { Date_Created = DateTime.Now };
db.CleanSupplierClaimsUploadSessions.Add(cleanGPSessionObject);
db.SaveChanges();
var sessionID = cleanGPSessionObject.Id;
//Here I write all the records includin the ID I got from the foreign key table
foreach (var claim in supplierClaimsData)
{
db.CleanSupplierClaims.Add(new CleanSupplierClaim
{
Id = claim.Id,
ST_Key = claim.ST_Key,
Action = claim.Action,
ClaimReference = claim.ClaimReference,
Currency = claim.Currency,
Line_Number = claim.Line_Number,
Total_Claim = claim.Total_Claim,
Domain_Username = claim.Domain_Username,
DateCreated = DateTime.Now,
ImportFlag = false,
ReadyForImport = false,
CleanSupplierClaimsUploadSessionID = sessionID//Foreging key
});
db.SaveChanges();
}
}
}
catch (FileLoadException ex)
{
ViewData["error"] = ex.Message;
}
TempData["supplierClaimsData"] = supplierClaimsData;
//Here I attempt to read the recorsds I just wrote with the iD
var claimsRecords = db.CleanSupplierClaims.Include(c => c.CleanSupplierClaimsUploadSession);
//return RedirectToAction("ValidateClaims", "Home", supplierClaimsData);
return View("Index", claimsRecords.ToList());
}
public ActionResult ValidateClaims()
{
var claimsRecords = db.CleanSupplierClaims.Include(c => c.CleanSupplierClaimsUploadSession);
List<CleanSupplierClaim> supplierClaimsData = claimsRecords.ToList();// My issue is here, I get all records and not the ones the user just uploaded
CleanSupplierClaimData supplierClaimUplaod = new CleanSupplierClaimData();
The main problem is that I get all records and I just need the ones the user just uploaded using the foreign key, is there perhaps a better more sophisticated way to do this. your help will be greatly appreciated.

insert only new data instead duplicate the existing data

I have this code running calling the data from the core system,
public int MuatTurunMTS(hopesWcfRef.Manifest2 entParam, string destination, int capacity)
{
try
{
EL.iSeriesWcf.IiSeriesClient iClient = new iSeriesWcf.IiSeriesClient();
EL.iSeriesWcf.Manifest2 manifest2 = new iSeriesWcf.Manifest2();
manifest2 = iClient.GetManifest2(entParam.NOKT, destination, capacity);
DataTable dt = new DataTable();
dt = manifest2.Manifest2Table;
List<hopesWcfRef.Manifest2> LstManifest2 = new List<hopesWcfRef.Manifest2>();
for (int i = 0; i < dt.Rows.Count; i++)
{
hopesWcfRef.Manifest2 newDataRow = new hopesWcfRef.Manifest2();
newDataRow.NOPASPOT = dt.Rows[i][1].ToString();
newDataRow.NOKT = dt.Rows[i][0].ToString();
newDataRow.KOD_PGKT = dt.Rows[i][2].ToString();
newDataRow.KOD_KLGA = dt.Rows[i][3].ToString();
newDataRow.NAMA = dt.Rows[i][4].ToString();
newDataRow.TKHLAHIR = (dt.Rows[i][5].ToString() == "1/1/0001 12:00:00 AM" ? DateTime.Now : Convert.ToDateTime(dt.Rows[i][5])); //Convert.ToDateTime(dt.Rows[i][5]);
newDataRow.JANTINA = dt.Rows[i][6].ToString();
newDataRow.WARGANEGARA = dt.Rows[i][7].ToString();
newDataRow.JNS_JEMAAH = dt.Rows[i][8].ToString();
newDataRow.NO_SIRI = dt.Rows[i][9].ToString();
newDataRow.NO_MS = Convert.ToInt16(dt.Rows[i][10]);
newDataRow.BARKOD = dt.Rows[i][13].ToString();
newDataRow.NO_DAFTAR = dt.Rows[i][14].ToString();
//bydefault make cell empty
newDataRow.STS_JEMAAH = "";
newDataRow.SEAT_NO = "";
newDataRow.SEAT_ZONE = "";
newDataRow.BERAT = 0;
newDataRow.JUM_BEG = 0;
LstManifest2.Add(newDataRow);
}
int cntr = 0;
if (LstManifest2.Count != 0)
{
foreach (hopesWcfRef.Manifest2 manifest in LstManifest2)
{
cntr++;
SaveManifestTS(manifest);
}
}
return LstManifest2.Count;
}
catch (Exception ex)
{
throw ex;
}
}
And goes to :
public void SaveManifestTS(hopesWcfRef.Manifest2 manifest)
{
try
{
ent.BeginSaveChanges(SaveChangesOptions.Batch, null, null);
ent.AddObject("Manifest2", manifest);
ent.SaveChanges(SaveChangesOptions.Batch);
}
catch (Exception ex)
{
//Uri u = new Uri(ent.BaseUri + "GetErrorMsg"
// , UriKind.RelativeOrAbsolute);
//string datas = (ent.Execute<string>(u)).FirstOrDefault();
//Exception ex = new Exception(datas);
throw ex;
}
}
When SaveChanges run, if the data exist it will duplicate the entire row,
How to avoid the data being duplicate when insert (savechanges)??????
Many Thanks
What about: Do not insert.
In these cases I am using a MERGE statement that updates existing data (based on primary key) and inserts new data.
Oh, and all the code example you loved to post is totally irrelevant to the question, which is a pure SQL side question. You literally quote your car manual then asking which direction to turn.

Error when returning value in C# 2.0

I downloaded an example application for using some web services with an online system.
I am not sure if all code below is needed but it is what I got and what I am trying to do is to use the search function.
I start by calling searchCustomer with an ID I have:
partnerRef.internalId = searchCustomer(customerID);
And the code for searchCustomer:
private string searchCustomer(string CustomerID)
{
string InternalID = "";
CustomerSearch custSearch = new CustomerSearch();
CustomerSearchBasic custSearchBasic = new CustomerSearchBasic();
String nameValue = CustomerID;
SearchStringField entityId = null;
entityId = new SearchStringField();
entityId.#operator = SearchStringFieldOperator.contains;
entityId.operatorSpecified = true;
entityId.searchValue = nameValue;
custSearchBasic.entityId = entityId;
String statusKeysValue = "";
SearchMultiSelectField status = null;
if (statusKeysValue != null && !statusKeysValue.Trim().Equals(""))
{
status = new SearchMultiSelectField();
status.#operator = SearchMultiSelectFieldOperator.anyOf;
status.operatorSpecified = true;
string[] nskeys = statusKeysValue.Split(new Char[] { ',' });
RecordRef[] recordRefs = new RecordRef[statusKeysValue.Length];
for (int i = 0; i < nskeys.Length; i++)
{
RecordRef recordRef = new RecordRef();
recordRef.internalId = nskeys[i];
recordRefs[i] = recordRef;
}
status.searchValue = recordRefs;
custSearchBasic.entityStatus = status;
}
custSearch.basic = custSearchBasic;
SearchResult response = _service.search(custSearch);
if (response.status.isSuccess)
{
processCustomerSearchResponse(response);
if (seachMoreResult.status.isSuccess)
{
processCustomerSearchResponse(seachMoreResult);
return InternalID;
}
else
{
_out.error(getStatusDetails(seachMoreResult.status));
}
}
else
{
_out.error(getStatusDetails(response.status));
}
return InternalID;
}
In the code above processCustomerSearchResponse gets called
processCustomerSearchResponse(response);
The code for this function is:
public string processCustomerSearchResponse(SearchResult response)
{
string InternalID = "";
Customer customer;
customer = (Customer)records[0];
InternalID = customer.internalId;
return InternalID;
}
What the original code did was to write some output in the console but I want to return the InternalID instead. When I debug the application InternalID in processCustomerSearchResponse contains the ID I want but I don't know how to pass it to searchCustomer so that function also returns the ID. When I debug searchCustomer InternalID is always null. I am not sure on how to edit the code under response.status.isSuccess
to return the InternalID, any ideas?
Thanks in advance.
When you call processCustomerSearchResponse(response);, you need to store the return value in memory.
Try modifying your code like this:
InternalID = processCustomerSearchResponse(response);

Categories