Condition of if/else in C# not behaving as expected - c#

I'm experiencing weird behavior in an if/else statement written in C#. I am new to C# and a novice with ASP.NET using Visual Studio Pro 2017.
I am working on adding functionality to a web app to allow for uploading of SQL data to table via user uploading a .CSV per this article by Mudassar Khan
All works well with this. I am now working to validate the data before the upload takes place.I am using the same .CSV from the article above but with a few "error" conditions introduced.
1,John,Hammond,United States
2,Shouldbreak,India
3,JohnMathews,Ireland
4,Robert,Schidner,Russia,sdfsdfdf,sdfsdfsdf,
In the code below you will see that I am testing to be sure there are the correct number of columns in each row by setting the required number of columns in a variable and comparing that to the number of commas found in each row present in the .CSV.
My desired validation is as follows:
If there are a correct number of columns add to dataTable.
If there are not the correct amount of columns.
Display ASP control which contains the error message.
If there are too many columns.
Add a string to the error message that include the row number and the incorrect number of columns.
If there are too few columns.
Add a string to the error message variable that includes the row number and the incorrect number of columns.
I am also tracking and displaying the number of commas found in each row in a separate ASP control for debugging purposes.
Below is the code in my code-behind file:
//Upload and save the file
string csvPath = Server.MapPath("~/_uploadedDataCSV/") + Path.GetFileName(FileUpload1.PostedFile.FileName);
FileUpload1.SaveAs(csvPath);
DataTable dt = new DataTable();
int columnCount = 4; // Expected number of columns
int commas = 0; // Set comma count to 0;
int tableRow = 0; // set current table row to 0;
Count.Text = string.Empty;
string errMessage = string.Empty;
dt.Columns.AddRange(new DataColumn[4] // Add expected number of columns to table
{
// Add column Header and expected (c#)datatype
new DataColumn("Id", typeof(int)),
new DataColumn("First Name", typeof(string)),
new DataColumn("Last Name", typeof(string)),
new DataColumn("Country",typeof(string))
});
string csvData = File.ReadAllText(csvPath); // Creates String of all content in CSV file
foreach (string row in csvData.Split('\n')) // Do the following for each row in the file
{
// Start Validation Logic
// - - - - - - - - - - - - - - - - - - - -
tableRow++;
// Count commas in row
foreach (char ch in row)
{
if (ch.Equals(','))
{
commas++;
}
}
if (!string.IsNullOrEmpty(row)) // if the row is not empty
{
if (commas == columnCount) // if commas in row equals the number of columns expected
{
dt.Rows.Add();
int i = 0;
foreach (string cell in row.Split(','))
{
dt.Rows[dt.Rows.Count - 1][i] = cell;
i++;
}
}
//Begin error reporting for row
// - - - - - - - - - - - - - - - - - - - -
else if (commas != columnCount)
{
err.Visible = true; // show error message element
err.Text = errMessage; // set text of error message element to value of errMessage variable
if (commas >= columnCount) // if too many columns in row
{
errMessage+= $"Row {tableRow} has too many columns <br />";
}
else if (commas < columnCount) // if not enough columns in row
{
errMessage+= $"Row {tableRow} does not have enough columns <br />";
}
}
}
Count.Text+= commas.ToString() + "|";
commas = 0;
}
The issue I am having is when there are too many columns in a row, I am not getting the error message associated with this condition. The debug controller properly displays the number of commas and if there are too few, displays the message properly, just not the message for too many. When I step through the code in debugger the variables (commas / columnCount) are reported corrected for all the rows.
Any suggestions? I am happy to provide more information if I left anything out.
I have tried removing the condition of too few columns and it still doesn't display the message for too many columns. I have also tried reversing the order of the check but the same output is given. I have tried rebuilding per this discussion.

All credits should go to #Mike McCaughan, his comment is absolutely in place. I just want to give a little example as of what was stated by Mike:
string a = string.Empty;
string b = a;
a += "3";
Console.WriteLine(b); //result in empty string in console
back to your problem:
string errMessage = string.Empty;
err.Text = errMessage;
errMessage+= $"Row {tableRow} has too many columns <br />";
Console.WriteLine(err.Text); //result in empty string in console
The reason why it happens is because of the immutable nature of string in c#. Means any changes to the string object will result in a new object creation. You can find more information there: Immutability of String Objects

Related

Validate column with Excel formula increments the formula is expected behavior or bug?

I'm trying to create a spreadsheet where the first sheet ("Catalog") contains some pre-filled and some empty values in a column. I want the values to be in a drop down list that are restricted to values found in the second sheet ("Products").
I would expect that if I set the the Excel validation formula for cells "A1:A1048576" in the "Catalog" sheet to be a list validation of "Products!A1:A100" that every cell would only allow values from "Products!A1:A100". However, I'm finding that my formula gets incremented for every row in the "Catalog" sheet (i.e. In row 2 the formula becomes "Products!A2:A101", in row 3 the formula becomes "Products!A3:A102").
If version matters I'm using EPPlus.Core v1.5.4 from NuGet.
I'm not sure if this is a bug or if I'm going about applying my formula wrong?
I've already tried directly applying the validation to every cell in the column one cell at a time. I found that not only does it moderately increase the size of the resulting Excel file but more importantly it also exponentially increases the time taken to generate the Excel file. Even applying the validation one cell at a time on the first 2000 rows more than doubles the generation time.
ExcelPackage package = new ExcelPackage();
int catalogProductCount = 10;
int productCount = 100;
var catalogWorksheet = package.Workbook.Worksheets.Add($"Catalog");
for (int i = 1; i <= catalogProductCount; i++)
{
catalogWorksheet.Cells[i, 1].Value = $"Product {i}";
}
var productsWorksheet = package.Workbook.Worksheets.Add($"Products");
for (int i = 1; i <= productCount; i++)
{
productsWorksheet.Cells[i, 1].Value = $"Product {i}";
}
var productValidation = catalogWorksheet.DataValidations.AddListValidation($"A1:A1048576");
productValidation.ErrorStyle = ExcelDataValidationWarningStyle.stop;
productValidation.ErrorTitle = "An invalid product was entered";
productValidation.Error = "Select a product from the list";
productValidation.ShowErrorMessage = true;
productValidation.Formula.ExcelFormula = $"Products!A1:A{productCount}";
I guess I'm not that adept at Excel formulas.
Changing this line:
productValidation.Formula.ExcelFormula = $"Products!A1:A{productCount}";
to this:
productValidation.Formula.ExcelFormula = $"Products!$A$1:$A${productCount}";
stopped the auto increment issue. Hopefully this answer will save someone else some sanity as I wasted half a day on this issue myself.

c# Read lines from a file and replace with text from DataGridView Data

I am relatively new to c#, I am creating an windows application which would read all the lines from a text file. The user will input the string which needs to be replaced in Column[0] and the text with which it needs to be replaced in Column1 of the DataGridView control.
I have created two string arrays column0 and column1.
However, I am getting an error while replacing the string in line (column0, column1)
The following is my code:
string[] column0 = new string[dgvMapping.Rows.Count];
string[] column1 = new string[dgvMapping.Rows.Count];
int j = 0;
foreach(DataGridViewRow row in dgvMapping.Rows)
{
if (!string.IsNullOrEmpty(Convert.ToString(row.Cells[0].Value)))
{
column0[j] = Convert.ToString(row.Cells[0].Value);
column1[j] = Convert.ToString(row.Cells[1].Value);
j++;
}
}
var _data = string.Empty;
String[] arrayofLine = File.ReadAllLines(ofd.FileName);
using (StreamWriter sw = new StreamWriter(ofd.FileName + ".output"))
{
for (int i = 0; i < arrayofLine.Length; i++)
{
string line = arrayofLine[i];
line = line.Replace(column0[i], column1[i]);
sw.WriteLine(line);
}
}
I am using OpenFileDialog to select the file.
The Error While Executing:
You are looping around a file of unknown number of lines, and assuming that the count of lines in the grid is exactly the same as that of the file. Your code will only work if both the file and the gridView have the same number of lines.
One of the solutions, is to loop over the array of lines (as you have already did), and search for the GridViewRow in which the current line contains a key in your DGV. If this is the case, then replace all the occurences of the key by the value (obtained from the gridView) in that line, otherwise do nothing.
Check out the code below :
// Convert the row collection to a list, so that we could query it easily with Linq
List<DataGridViewRow> mySearchList = dataGridView1.Rows.Cast<DataGridViewRow>().ToList();
const int KEY_INDEX = 0; // Search index in the grid
const int VALUE_INDEX = 1; // Value (replace) index in the grid
for (int i = 0; i < arrayofLines.Length; i++)
{
string line = arrayofLines[i];
// Get data grid view Row where this line contains the key string
DataGridViewRow matchedRow = mySearchList.FirstOrDefault(obj => line.Contains(obj.Cells[KEY_INDEX].Value.ToString()));
// If this row exists, replace the key with the value (obtained from the grid)
if (matchedRow != null)
{
string key = matchedRow.Cells[KEY_INDEX].Value.ToString();
string value = matchedRow.Cells[VALUE_INDEX].Value.ToString();
line = line.Replace(key, value);
sw.WriteLine(line);
}
else
{
// Otherwise, do nothing
}
}
Stuartd is correct… there are more lines in the file than there are elements to search. I am not sure what the search is doing in a sense that it seems somewhat limited. The code appears to search for each item depending on what line it is. The searched value in column 0 and the replace value in column 1 of row 0… will only replace those values for the FIRST line in the file. The DataGridViews second row values will search/replace only the SECOND line and so on. This seems odd.
Example the two string arrays (column0 and column1) have sizes set to the number of rows in dgvMapping. Let’s say there are 5 rows in the grid, then the array sizes will be 5 strings. When you start the loop to write the strings, the loop starts at 0 and stops at the number of lines in the file. The code uses this i variable as an index into the two arrays. If there are more lines in the file, than there are rows in the grid… then you will get the error.
Again, this seems odd to do the search and replace this way. Assuming you want to search for EACH term in all the rows in column 0 and replace the found searched string with the replace string in column 1, then you will need to loop through EACH row of the grid for EACH line in the file. This will replace ALL the search/replace terms in the grid with ALL the lines in the file. If this is what you what to accomplish below is one way to achieve this, however…there are possibly better ways to accomplish this.
The code below reads the file into one big string. Then the code loops through ALL the grid rows to search/replace the strings in the big string. Hope this helps.
string bigString = File.ReadAllText(ofd.FileName);
try {
using (StreamWriter sw = new StreamWriter(ofd.FileName + ".output")) {
for (int k = 0; k < dgvMapping.Rows.Count; k++) {
if (dgvMapping.Rows[k].Cells[0].Value != null && dgvMapping.Rows[k].Cells[1].Value != null) {
string searchTerm = dgvMapping.Rows[k].Cells[0].Value.ToString();
string replaceTerm = dgvMapping.Rows[k].Cells[1].Value.ToString();
if (searchTerm != "") {
bigString = bigString.Replace(searchTerm, replaceTerm);
} else {
// one of the terms is empty
}
} else {
// one of the terms is null}
}
}
sw.WriteLine(bigString);
}
}
catch (Exception ex) {
MessageBox.Show("Write Erro: " + ex.Message);
}

jQuery calculate column count from excel range

I have a C# web form where a user inputs data about an excel file into a number of text boxes, including:
TXT_FirstDataRow - integer field which represents the first row that contains data
TXT_LastDataColumn - string field which represents the last column that contains data
TXT_Range - string field which contains the calculated data range using the above two fields
TXT_ColumnCount - The number of columns in the calculated range
I am currently using jQuery to automatically calculate the data range using the code below which works perfectly:
$('#TXT_FirstDataRow,#TXT_LastDataColumn').keyup(function () {
var row = $('#TXT_FirstDataRow').val();
var column = $('#TXT_LastDataColumn').val();
var range = 'A' + row + ':' + column; //All ranges start at column A
$('#TXT_Range').val(range);
});
I would now like to automatically populate the TXT_ColumnCount with the count of the columns. For example:
Range |Column Count
------+------------
A1:C |3
A7:BX |76
I imagine that this should be made easier as the ranges will always be starting from column A, so the column count should just be the equivalent column number of TXT_LastDataColumn, but I am afraid even this is beyond my slightly limited jQuery knowledge.
Any help would be greatly appreciated.
Thanks
I found an answer on SO that helped me, link below:
Convert excel column alphabet (e.g. AA) to number (e.g., 25)
function letterToNumbers(string) {
string = string.toUpperCase();
var letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', sum = 0, i;
for (i = 0; i < string.length; i++) {
sum += Math.pow(letters.length, i) * (letters.indexOf(string.substr(((i + 1) * -1), 1)) + 1);
}
return sum;
}

C# Datatable.select fails with # hash character in data

I have a datatable containing a list of service calls placed each month. Column F4 contains the serial number of the machine, now all I wanted to do was find the number of service calls placed for each serial number and add the count to a new a column. I wrote the code below and all was working fine but then a problem arose when a hash sign '#' appeared in the data to reflect an unknown or yet assigned serial number. This caused the error "Cannot perform '=' operation on System.String and System.Int32." on the line "DataRow[] FoundRow = dt.Select("[F4] =" + chkStr);
The F4 column is a text/string and I even tried cloning the datatable and manually assigning the column to .DataType = typeof(String); I know its the # sign giving the issue as if I remove from the data for testing purposing all is fine. Can anyone shed some light on this. Many thanks.
// datatable dt contains all my monthly call data
dt.Columns.Add("Counter", typeof(System.Int32)); // add a count column to the datatable
for (int i = 0; i < dt.Rows.Count; i++) //loop through the datatable
{
string chkStr = dt.Rows[i]["F4"].ToString(); // get 1st serial num, then each other one in loop
DataRow[] FoundRows = dt.Select("[F4] =" + chkStr); // OK now select all the table rows containing this serial, place in FoundRows
Int32 count = FoundRows.Length; // get the length which is a count of the same serial num / service calls
dt.Rows[i]["Counter"] = count;
}
Don't worry about counting the value as in Int32. Who cares; you can still "use" the number when it is a string. Read it as a string, "#" will be valid and numbers will be valid. Here is a simple solution that will even tell you how many have not been assigned yet.
public class GroupCounter {
private Dictionary<string, int> serialNumbers;
public GroupCounter() {
this.serialNumbers = new Dictionary<string, int>();
}
public Dictionary<string, int> Count(DataTable table) {
foreach (DataRow row in table.Rows) {
int counter;
string serial = row.Field<string>("COLUMN");
if(!this.serialNumbers.TryGetValue(serial, out counter)){
this.serialNumbers.Add(serial, 1);
}
counter++;
}
return this.serialNumbers;
}
}
Since this returns a Dictionary you can use dictionary[key] (your serial number) to fetch to count for any serial number.
many thanks for your suggestions but found the answer was simply to add single quote marks ' around the chkStr as below and the code then handled the # hash characters fine. Appreciate your time.
DataRow[] FoundRows = dt.Select("[F4] ='" + chkStr + "'");

Weird issue when removing columns from a datatable

I want to remove every column after the 3rd column from a CSV file loaded into a datatable, but I'm getting odd results. Here's my code.
System.Data.DataTable csv_datatable = null;
using (System.IO.StreamReader re = new System.IO.StreamReader(model.file.InputStream))
{
csv_datatable = CsvParser.Parse(re as System.IO.TextReader);
for (int x = 3; x < csv_datatable.Columns.Count + 1; x++)
{
csv_datatable.Columns.RemoveAt(x);
}
}
My sample CSV file:
7 columns, and I want to keep the first three.
email,first,last,middle,nick,job,phone
roryok#fakedomain.com,rory,wally,ok,danger,developer,555-0123
This is the result I get.
email,first,last,nick,phone
roryok#fakedomain.com,rory,ok,danger,555-0123
As you can see, rather than removing the last columns as I would expect, the code actually removes the 4th and 6th columns.
As usual, figured this out as I was posting, but I'll post the solution anyway in case it helps someone.
As each column is removed, the index for each column changes. So when you remove column 3, 4 becomes the new 3. This code works:
for (int x = 0; x < csv_datatable.Columns.Count; x++)
{
csv_datatable.Columns.RemoveAt(3);
}
This will loop over the number of columns, and remove the third column over and over again until everything is gone.

Categories