Got an error message for "Specified cast is not valid" - c#

I am trying to compute the sum for same IDs in each column in the data table. In the datatable there are empty elements. When I run the following code to the line calculating colP_sum, it gives me the error if "specific cast is not valid". It seems caused by the empty elements in the data table? How should I solve it? I am sure if the datatable is filled up with numbers this code works.
for (int i = 0; i < LoadIDcount; i++)
{
string IDnow = LoadID[i, 0];
string IDsaved = LoadP_dt.Rows[i][0].ToString();
if (LoadP_dt.Rows[i][0].ToString() == IDnow)
{
for (int j = 0; j < 8760; j++)
{
string colPnow = SP_dt.Columns[j * 2 + 4].ColumnName.ToString();
double ColP_sum = (double)SP_dt.Compute(String.Format("Sum([{0}])", colPnow), String.Format("Load_ID = '{0}'", IDnow));
string colQnow = SP_dt.Columns[j * 2 + 5].ColumnName.ToString();
double ColQ_sum = (double)SP_dt.Compute(String.Format("Sum([{0}])", colQnow), String.Format("Load_ID = '{0}'", IDnow));
LoadP_dt.Rows[i][j + 2] = ColP_sum;
LoadQ_dt.Rows[i][j + 2] = ColQ_sum;
Console.WriteLine("{0} {1}", i, j);
}
}
else
{
Console.WriteLine("ID does not match");
}
}
CSVfilewriter(CSVPpath, LoadP_dt);//save the Load_P datatable to CSV file
CSVfilewriter(CSVQpath, LoadQ_dt);//save the Load_Q datatable to CSV file
//CSVfilewriter(CSVSPpath, SP_dt);//save the service point datatable to CSV file
}

if "colPnow" is not a number, that could explain it: the "Compute" and "Sum" both appear to be expecting number value

Related

C# array returns value by foreach, not by array index

I have below code:
String[] splititemcol;
String[] splititem;
String singlestring;
while (reader.Read())
{
splititemcol = reader.GetValue(2).ToString().Split((char)16); //split each item
for (int i = 0; i < splititemcol.Count(); i++)
{
splititem = splititemcol[i].ToString().Split((char)14);
resultstr.Append("<tr><td>" + splititem[0] + "</td><td>");
singlestring = "";
for(int k=0;k<splititem.Count();k++)
{
if(k==2)
{
singlestring = splititem[k].ToString();
break;
}
}
resultstr.Append(singlestring + "</td></tr>");
}
}
In above code I could get value of 3rd splititem.
String[] splititemcol;
String[] splititem;
String singlestring;
while (reader.Read())
{
splititemcol = reader.GetValue(2).ToString().Split((char)16); //split each item
for (int i = 0; i < splititemcol.Count(); i++)
{
splititem = splititemcol[i].ToString().Split((char)14);
resultstr.Append("<tr><td>" + splititem[0] + "</td><td>");
singlestring =splititem[2].ToString();
resultstr.Append(singlestring + "</td></tr>");
}
}
In above code I try to get value of 3rd splititem only by array index i.e. without foreach.
But it throws Index was outside the bounds error on line 9 as below.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.IndexOutOfRangeException: Index was outside the bounds of the array.
When I test splititem.Count it shows 4.
EDIT:
I manually store every value of the array in variable as like this and all are return values. So I come to the conclusion that either we have to iterate array by for loop or this script is stuck due to my strange delimiter in the strings (character 14).
String[] splititemcol;
String[] splititem;
String singlestring;
String item="";
String weight="";
String quantity="";
String amount="";
while (reader.Read())
{
splititemcol = reader.GetValue(2).ToString().Split((char)16); //split each item
for (int i = 0; i < splititemcol.Count(); i++)
{
splititem = splititemcol[i].ToString().Split((char)14);
resultstr.Append("<tr><td>" + splititem[0] + "</td><td>");
singlestring = "";
for(int k=0;k<splititem.Count();k++)
{
if (k == 0)
item = splititem[k].ToString();
else if (k == 1)
weight = splititem[k].ToString();
else if (k == 2)
quantity = splititem[k].ToString();
else if (k == 3)
amount = splititem[k].ToString();
}
resultstr.Append(weight + "</td><td>" + quantity + "</td><td>" + amount + "</td></tr>");
}
}
Thank you for all you guys try to give solution in this issue.
That means splititem[2] doesn't exists. In your first case you have the below condition which considering only lines which will have splititem[2] but in second case you are trying to access index 2 directly resulting in exception
if(k==2)
{
singlestring = splititem[k].ToString();
break;
}
Compare this (from first snippet):
if(k==2)
{
singlestring = splititem[k].ToString();
break;
}
with this (from the second snippet):
singlestring =splititem[2].ToString();
In the first case you make a check and provided that k==2 is true you read the corresponding value. That misses from the second case and nobody guarantees that splititem has a Length of 3.

C# StreamRead in CSV file with a field containing ","

I'm working in closed envrionment where I cannot install additional packages and have limited ability to use .Net framework classes. Plus I have no control over the CSV file format that I'm receiving.
I receive a CSV file that must be pulled into our business system and updates the database.
I can pull the file in to a DataTable via the below code ...
CSV File Ex:
Order# Qty Description ...
12345 3 desc1, desc2, desc3, etc..
while (!sr.EndOfStream)
{
string[] rows = sr.ReadLine().Split(',');
DataRow dr = dt.NewRow();
for (int i = 0; i < rows.Length; i++)
{
dr[i] = rows[i];
}
dt.Rows.Add(dr);
}
However, the problem is that one field in the CSV file is a description that contains multiple "," characters. Doing the above loads each comma separated word set in the description value into its own index in the rows array.
Currently there should be a total of 10 columns in the csv file but with the description field issue the number of columns vary depending on the length/number of commas in the description field...10, 15, 22 columns etc.
I have no control over the format of the CSV file before it's sent. Is there any way to get around this. Even skipping over this field when creating the DataTable would be fine for my purposes.
Thanks
You can use textqualifier to enclose every field so that the commas or semicolons are not considered as delimeters. The following method should fix the problem.
Install-Package CsvHelper
public static DataTable ReadCSVToDataTable(string path)
{
CsvHelper.Configuration.CsvConfiguration config = new CsvHelper.Configuration.CsvConfiguration();
config.Delimiter = delimeter;
config.Encoding = new UTF8Encoding(false);
if (string.IsNullOrEmpty(textQualifier))
{
config.QuoteAllFields = false;
}
else
{
char qualifier = textQualifier.ToCharArray()[0];
config.Quote = qualifier;
config.QuoteAllFields = true;
}
DataTable dt = new DataTable();
using (var sr = new StreamReader(path))
{
using (var reader = new CsvReader(sr, config))
{
int j = 0;
while (reader.Read())
{
if (j == 0)
{
if (config.HasHeaderRecord)
{
foreach (string header in reader.FieldHeaders)
dt.Columns.Add(header);
}
else
{
for (int i = 0; i < reader.CurrentRecord.Length; i++)
dt.Columns.Add();
}
j++;
}
AddRow(dt, reader);
}
}
}
return dt;
}
Fstagger, this should work for you assuming you have only one column with internal comma's and the CSV is formed properly (especially if the Description field begins with ," and ends with ",. You need to replace my example INDEX_OF_DESCRIPTION with the actual value.
int iDescStart = 0;
int iDescEnd = 0;
string zLine = "";
const int INDEX_OF_DESCRIPTION = 3;
const char SEPARATOR = '\u001F'; //ASCII Unit Separator, decimal 31
while(!sr.EndOfStream){
zLine = sr.ReadLine();
iDescStart = zLine.IndexOf(",\"");
iDescEnd = zLine.IndexOf("\",");
zLine = zLine.Substring(0, iDescStart)
+ ","
+ zLine.Substring(iDescStart + 2, iDescEnd - iDescStart - 2).Replace(',', SEPARATOR)
+ ","
+ zLine.Substring(iDescEnd + 2);
string[] zaFields = zLine.Split(',');
zaFields[INDEX_OF_DESCRIPTION] = zaFields[INDEX_OF_DESCRIPTION].Replace(SEPARATOR, ',');
datarow dr = dt.NewRow();
for (int i = 0; i < zaFields.Length; i++){
dr[i] = zaFields[i];
}
dt.Rows.Add(dr);
}
Let me know if this works for you : )
It looks like your CSV has fixed size columns padded with spaces. So I guess you'd be better off reading a fixed amount of characters for each column and trim the trailing spaces, instead of splitting with comma.
Try this class.
It deals with commas how you need.
My Solution that ended up working
while (!sr.EndOfStream)
{
string[] rows = sr.ReadLine().Split(',');
var fullrow = String.Empty;
foreach (var entry in rows)
{
fullrow += entry.ToString() + ",";
}
var startQuote = fullrow.IndexOf("\"");
var endQuote = fullrow.IndexOf("\"", startQuote + 1); //LastIndexOf("\"");
if (startQuote > -1 && endQuote > -1)
{
var substring = fullrow.Substring(startQuote, Math.Abs(startQuote - endQuote));
substring = substring.Replace(',', ' ');
fullrow = fullrow.Remove(startQuote, Math.Abs(startQuote - endQuote)).Insert(startQuote, substring);
}
rows = fullrow.Split(',');
DataRow dr = dt.NewRow();
for (int i = 0; i < rows.Length; i++)
{
dr[i] = rows[i];
}
dt.Rows.Add(dr);
}
Thanks #Michael Gorsich for the alternate code!

Why do I get reading numeric values as string in excel

I try to read excel file using NPOI library.
Here is the code:
public void ReadDataFromXL()
{
try
{
for (int i = 1; i <= sheet.LastRowNum; i++)
{
IRow row = sheet.GetRow(i);
for (int j = 0; j < row.Cells.Count(); j++)
{
var columnIndex = row.GetCell(j).ColumnIndex;
var cell = row.GetCell(j);
if (cell != null)
{
switch (cell.CellType)
{
case CellType.Numeric:
var val = cell.NumericCellValue; ;
break;
case CellType.String:
var str = cell.StringCellValue;
break;
}
}
}
}
}
catch (Exception)
{
throw;
}
}
Here the content of .xlsx file that I try to read:
As you can see column X and column Y are numeric columns.
But when I start to read this columns using the code above some of the numeric values in X and Y column have been recognizes by code as string values.
For example in picture above the cell B4 is numeric type but, on cell.CellType it shows String and the value of the string is 31.724732480727\n. '\n' is appended to the value.
Any idea why some numeric values appeared as string and why '\n' appended to the value?
It looks like the datatype of the column is of String, so if you wanted to check for the double datatype (assuming its going to be in the num+'\n' format, you could try the following snippet of code.
String number = "1512421.512\n";
double res;
if (double.TryParse(number.Substring(0, number.Length - 1), out res))
{
Console.WriteLine("It's a number! " + res);
}

Try...catch returning nothing but code is still breaking

UPDATE: So this code is collection a SQL Query into a DataSet prior to this method. This data set is then dropped into excel in the corresponding tab at a specific cell address(which is loaded from the form) but the code below is the exporting to excel method. I am getting the following error:
An unhandled exception of type 'System.AccessViolationException' occurred in SQUiRE (Sql QUery REtriever) v1.exe
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
I have been tracking this for a while and thought I fixed it, but my solution was a false positive. So I am using a try...catch block that is breaking but not returning anything. Let me know if you all see anything that I am missing. I usually break on this line (templateSheet = templateBook.Sheets[tabName];) and on the same tabName. The tab is not locked or restricted so It can be written to and works more than half of the time.
public void ExportToExcel(DataSet dataSet, Excel.Workbook templateBook, int i, int h, Excel.Application excelApp) //string filePath,
{
try
{
lock (this.GetType())
{
Excel.Worksheet templateSheet;
//check to see if the template is already open, if its not then open it,
//if it is then bind it to work with it
//if (!fileOpenTest)
//{ templateBook = excelApp.Workbooks.Open(filePath); }
//else
//{ templateBook = (Excel.Workbook)System.Runtime.InteropServices.Marshal.BindToMoniker(filePath); }
//Grabs the name of the tab to dump the data into from the "Query Dumps" Tab
string tabName = lstQueryDumpSheet.Items[i].ToString();
templateSheet = templateBook.Sheets[tabName];
// Copy DataTable
foreach (System.Data.DataTable dt in dataSet.Tables)
{
// Copy the DataTable to an object array
object[,] rawData = new object[dt.Rows.Count + 1, dt.Columns.Count];
// Copy the values to the object array
for (int col = 0; col < dt.Columns.Count; col++)
{
for (int row = 0; row < dt.Rows.Count; row++)
{ rawData[row, col] = dt.Rows[row].ItemArray[col]; }
}
// Calculate the final column letter
string finalColLetter = string.Empty;
string colCharset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int colCharsetLen = 26;
if (dt.Columns.Count > colCharsetLen)
{ finalColLetter = colCharset.Substring((dt.Columns.Count - 1) / colCharsetLen - 1, 1); }
finalColLetter += colCharset.Substring((dt.Columns.Count - 1) % colCharsetLen, 1);
/*Grabs the full cell address from the "Query Dump" sheet, splits on the '=' and
*pulls out only the cell address (i.e., "address=a3" becomes "a3")*/
string dumpCellString = lstQueryDumpText.Items[i].ToString();
string dumpCell = dumpCellString.Split('=').Last();
/*Refers to the range in which we are dumping the DataSet. The upper right hand cell is
*defined by 'dumpCell'and the bottom right cell is defined by the final column letter
*and the count of rows.*/
string firstRef = "";
string baseRow = "";
//Determines if the column is one letter or two and handles them accordingly
if (char.IsLetter(dumpCell, 1))
{
char[] createCellRef = dumpCell.ToCharArray();
firstRef = createCellRef[0].ToString() + createCellRef[1].ToString();
for (int z = 2; z < createCellRef.Count(); z++)
{ baseRow = baseRow + createCellRef[z].ToString(); }
}
else
{
char[] createCellRef = dumpCell.ToCharArray();
firstRef = createCellRef[0].ToString();
for (int z = 1; z < createCellRef.Count(); z++)
{ baseRow = baseRow + createCellRef[z].ToString(); }
}
int baseRowInt = Convert.ToInt32(baseRow);
int startingCol = ColumnLetterToColumnIndex(firstRef);
int endingCol = ColumnLetterToColumnIndex(finalColLetter);
int finalCol = startingCol + endingCol;
string endCol = ColumnIndexToColumnLetter(finalCol - 1);
int endRow = (baseRowInt + (dt.Rows.Count - 1));
string cellCheck = endCol + endRow;
string excelRange;
if (dumpCell.ToUpper() == cellCheck.ToUpper())
{ excelRange = string.Format(dumpCell + ":" + dumpCell); }
else
{ excelRange = string.Format(dumpCell + ":{0}{1}", endCol, endRow); }
//Dumps the cells into the range on Excel as defined above
templateSheet.get_Range(excelRange, Type.Missing).Value2 = rawData;
/*Check to see if all the SQL queries have been run from
if (i == lstSqlAddress.Items.Count - 1)
{
//Turn Auto Calc back on
excelApp.Calculation = Excel.XlCalculation.xlCalculationAutomatic;
/*Run through the value save sheet array then grab the address from the corresponding list
*place in the address array. If the address reads "whole sheet" then save the whole page,
*else set the addresses range and value save that.
for (int y = 0; y < lstSaveSheet.Items.Count; y++)
{
MessageBox.Show("Save Sheet: " + lstSaveSheet.Items[y] + "\n" + "Save Address: " + lstSaveRange.Items[y]);
}*/
//run the macro to hide the unused columns
excelApp.Run("ReportMakerExecute");
//save excel file as hospital name and move onto the next
SaveTemplateAs(templateBook, h);
}
}
}
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}

Data truncation message when inserting data from one row to another row

Is there a way to allow data to get truncated if its too long when inserting it from one
row to another row? Whats happening now is that it gets an error and doesn't add the row if one of the fields is too long.
Here is the piece of code that I have:
DataRow dr = dt.NewRow();
for (int j = 0; j < edi50.Columns.Count; j++)
dr[j] = dr50[j];
dt.Rows.Add(dr);
try
{
RemoveNulls(dt);
daEDI40050.Update(dt);
}
catch (Exception e)
{
string m = e.Message;
}
I have a description field 25 chars long but the data is 34 chars going into it. I want to be able to have it insert the 25 and truncate the rest and still add the row.
thank you
You could get the schema from database first(untested):
DataTable schema;
using (var con = new System.Data.SqlClient.SqlConnection(conStr))
{
var getSchemaSql = String.Format("SELECT * FROM {0}", tableName);
using (var schemaCommand = new System.Data.SqlClient.SqlCommand(getSchemaSql, con))
{
con.Open();
using (var reader = schemaCommand.ExecuteReader(CommandBehavior.SchemaOnly))
{
schema = reader.GetSchemaTable();
}
}
}
and then something similar to this:
for (int j = 0; j < schema.Rows.Count; j++)
{
DataRow schemaRow = schema.Rows[j];
Type dataType = schemaRow.Field<Type>("DataType");
int columnSize = schemaRow.Field<int>("ColumnSize");
if (dataType.FullName == "System.String")
{
String value = dr50[j] as String;
if (value != null)
value = value.Substring(0, columnSize);
}
}
Again, totally untested and written from scratch, but it might give you an idea how to get the column size. Of course this works only for string, but i assume that this is what you want.
If you know which column has the limit, and which field that column maps to, then simply truncate the field's value on all objects before calling Update():
myObject.StringField = myObject.StringField.Substring(0,25);

Categories