Array.Copy() does not work on DataRow in C# - c#

I've got two arrays, and I need to put the second forward items to the other array using Array.Copy, but nothing happens, it just does not add anything.
Here's the code:
DataRow[] auxRows = rFComDataSet.TestStepNames
.Select("ScenarioName = '" + scenarioName + "'");
DataRow[] newRows = new DataRow[auxRows.Count()];
auxRows.CopyTo(newRows, 0);
foreach (DataRow row in newRows)
{
DataRow teste = this.rFComDataSet.TestStepNames.NewRow();
Array.Copy(row.ItemArray, 1, teste.ItemArray, 0, 4);
row["ScenarioName"] = newScenarioName;
this.rFComDataSet.TestStepNames.Rows.Add(row.ItemArray);
}

This behavior is the consequence of the implementation of the ItemArray property.
This is the code of the GET accessor
public object[] ItemArray
{
get
{
int num;
object[] objArray;
DataColumn column;
int num2;
num2 = this.GetDefaultRecord();
objArray = new object[this._columns.Count];
num = 0;
goto Label_0037;
Label_001C:
column = this._columns[num];
objArray[num] = column[num2];
num += 1;
Label_0037:
if (num < ((int) objArray.Length))
{
goto Label_001C;
}
return objArray;
}
}
As you can see calling DataRow.ItemArray returns a new object array where the values from the underlyng row are copied to.
When you use Array.Copy you are setting values in this array not in
the underlying values of the DataRow. So your row remains with the null values
A possible workaround is the following (NOT TESTED)
object[] itemArray = new object[this.rFComDataSet.TestStepNames.Columns.Count];
Array.Copy(row.ItemArray, 1, itemArray, 0, 4);
this.rFComDataSet.TestStepNames.Rows.Add(itemArray);
In this way we force the underlying values of new row created by Rows.Add to be the value of the object array created separately
There are a couple of things to take note however. Your call auxRows.CopyTo(newRows, 0); doesn't create a new row, it just copy all the rows reference to the new array, but they points at the same data, so changing anything in newRows change the corresponding row in auxRows.
Finally it is not clear why you have all this work to copy the row and then add to the TestStepNames table the same row from the foreach loop

Just skip Copy and do like:
DataRow teste = this.rFComDataSet.TestStepNames.NewRow();
teste.ItemArray = row.ItemArray;
row.ItemArray will create a new object for you.

Related

How to query DataTable

I have a datatable like that:
column1 column2 column3
a b c
d e f
I want to get index numbers of the cell "e" and i wrote these
int[] indexrowcol = new int[2];
for (int i = 0; i < dt.Columns.Count ; i++)
{
for (int j = 0; j < dt.Rows.Count; j++)
{
if (dt.Rows[i][j] == "e")
{
indexrowcol[0] = j; indexrowcol[1] = i;
}
}
}
How to write the same thing with usin LINQ? thanks.
I don't believe you have your original code implemented correctly to get what you're after. But at least it's more or less clear what you're trying to do. Here's some commented link code that can accomplish it.
var valToSearch = "e";
int[] indexrowcol = dt.AsEnumerable() // allows you to use linq
.SelectMany((row,rix) => // like 'Select', but stacks up listed output
row.ItemArray.Select( // ItemArray gets the row as an array
(col,cix) => new { rix, cix, value = col.ToString() }
)
)
.Where(obj => obj.value == valToSearch)
.Select(obj => new int[] { obj.rix, obj.cix })
.FirstOrDefault();
When I use the above code on the following DataTable, I get the result [1,1], which is the same result I get using your original code when I correct for the i/j reversal that existed at the time of this writing.
var dt = new DataTable();
dt.Columns.Add("Column1");
dt.Columns.Add("Column2");
dt.Columns.Add("Column3");
DataRow rw = dt.NewRow();
rw["Column1"] = "a";
rw["Column2"] = "b";
rw["Column3"] = "c";
dt.Rows.Add(rw);
rw = dt.NewRow();
rw["Column1"] = "d";
rw["Column2"] = "e";
rw["Column3"] = "f";
dt.Rows.Add(rw);
The reason your original code isn't quite right is that you use 'i' for columns and 'j' for rows, but then call dt.Rows[i][j], which is backwards. I highly recommend that your variables can be matched to what they are associated with. This is why I use names such as col, row, cix (column index), and rix to keep things straight.
In that vein, you might want to also output something other than an int[2]. Maybe a class or struct, or even just leave it as an anonymous object (get rid of the 'select' part of my query). Though I don't know your end use case, so I'll leave you alone on that.

How to add values in a validation list at once?

I'm trying to add values to a IExcelDataValidationList but the way I'm doing it right now is not very efficient.
I need to create many rows in my worksheet and populate a list for a certain cell for each of these rows. When I have many values to add to the list, this is taking forever.
for (var x = headerRowIndex + 1; x < 1000; x++)
{
var address = ExcelCellBase.TranslateFromR1C1($"R{x}C{colIndex}", 0, 0);
IExcelDataValidationList list = mainWorksheet.DataValidations.AddListValidation(address);
foreach (var e in values)
{
list.Formula.Values.Add(e);
}
}
You see how this can take a long time if values contain a lot of options.
Here's what I tried:
List<string> validationValues = new List<string>();
validationValues = values.ToList();
for (var x = headerRowIndex + 1; x < 1000; x++)
{
var address = ExcelCellBase.TranslateFromR1C1($"R{x}C{colIndex}", 0, 0);
var list = mainWorksheet.DataValidations.AddListValidation(address);
((List<string>)list.Formula.Values).AddRange(validationValues);
}
So I'm trying to add all values to the list at once. This compiles fine, but I'm getting this exception:
System.InvalidCastException: 'Unable to cast object of type
'DataValidationList' to type
'System.Collections.Generic.List`1[System.String]'.'
I've tried casting directly to DataValidationList but it's defined at private and only accessible by EPPlus itself.
Any ideas ?
Instead of creating IExcelDataValidationList per cell, create one for the whole column (address can include range of cells):
var address = ExcelCellBase.GetAddress(headerRowIndex + 1, colIndex, headerRowIndex + 1000, colIndex, true); // whole column
IExcelDataValidationList list = mainWorksheet.DataValidations.AddListValidation(address);
foreach (var e in values)
{
list.Formula.Values.Add(e);
}

How to convert a data row into an object array?

I have a data row which I would like to make into an object array since to add it to my data table I need an object array. What I have made up to now is this...
data_table.Rows.Add(data_row.ToArray<object>());
but this does not work since this does not give an object array, at least thats what my compiler tells me
You can use ItemArray property on DataRow type.
object[] arr = data_row.ItemArray;
You could always make an extension method to DataRow like this:
public static class DataRowExtension
{
// Class for: Conversion to object[]
public static object[] ToObjectArray(this DataRow dataRow)
{
// Identifiers used are:
int columnCount = dataRow.Table.Columns.Count;
object[] objectArray = new object[columnCount];
// Check the row is not empty
if (columnCount == 0)
{
return null;
}
// Go through the row to add each element to the array
for (int i = 0; i < columnCount; i++)
{
objectArray[i] = dataRow[i];
}
// Return the object array
return objectArray;
}
}
Extension methods are great.

C# - Specified cast is not valid using DataTable and Field<int>

I have a csv file with 8 columns, and I am trying to populate an object with 8 variables, each being a list to hold the columns in the csv file. Firstly, I am populating a DataTable with my csv data.
I am now trying to populate my object with the data from the DataTable
DataTable d = GetDataTableFromCSVFile(file);
CoolObject l = new CoolObject();
for (int i = 0; i < d.Rows.Count; i++)
{
l.column1[i] = d.Rows[i].Field<int>("column1"); <-- error here
}
And here is my CoolObject
public class CoolObject
{
public List<int> column1 { set; get; }
protected CoolObject()
{
column1 = new List<int>();
}
}
Unfortunately I am receiving an error on the highlighted line:
System.InvalidCastException: Specified cast is not valid
Why is this not allowed? How do I work around it?
Obviously you DataTable contains columns of type string, so do integer validation in GetDataTableFromCSVFile method, so consumers of this method don't need to worry about it.
Obviously you DataTable contains columns of type string, so do integer validation in GetDataTableFromCSVFile method, so consumers of this method don't need to worry about it.
private DataTable GetDataTableFromCSVFile()
{
var data = new DataTable();
data.Columns.Add("Column1", typeof(int));
// Read lines of file
// line is imaginery object which contains values of one row of csv data
foreach(var line in lines)
{
var row = data.NewRow();
int.TryParse(line.Column1Value, out int column1Value)
row.SetField("Column1", column1Value) // will set 0 if value is invalid
// other columns
}
return data;
}
Then another problem with your code, that you assugn new values to List<int> through index, where list is empty
l.column1[i] = d.Rows[i].Field<int>("column1");
Above line will throw exception because empty list doesn't have item on index i.
So you in the end your method will look
DataTable d = GetDataTableFromCSVFile(file);
CoolObject l = new CoolObject();
foreach (var row in d.Rows)
{
l.column1.Add(row.Field<int>("column1"));
}
In case you are using some third-party library for retrieving data from csv to DataTable - you can check if that library provide possibility to validate/convert string values to expected types in DataTable.
Sounds like someone didn't enter a number in one of the cells. You'll have to perform a validation check before reading the value.
for (int i = 0; i < d.Rows.Count; i++)
{
object o = d.rows[i]["column1"];
if (!o is int) continue;
l.column1[i] = (int)o;
}
Or perhaps it is a number but for some reason is coming through as a string. You could try it this way:
for (int i = 0; i < d.Rows.Count; i++)
{
int n;
bool ok = int.TryParse(d.rows[i]["column1"].ToString(), out n);
if (!ok) continue;
l.column1[i] = n;
}

for loop through ms sql columns

I'm coding in c# on webpages/razor with MS SQL database
I have a table with the following columns
Sat1
Sat2
Sat3
Sat4
...
Sat25
I want to loop through each of these, and assign the value to satAvail
I have the following
for (var i = 1; i < 26; i++)
{
satWeek = "Sat" + i;
satAvail = item.satWeek;
}
I want the equivalent of satAvail = item.Sat1;
I've tried a few different lines but having no joy
use reflection
var value = item.GetType().GetProperty("Sat" + i).GetValue(item, null);
and if you want a sum (assuming Sat1... Sat2 are integers)
var sum = 0;
for (var i = 1; i < 26; i++) {
sum +=(int)item.GetType().GetProperty("Sat" + i).GetValue(item, null);
}
satAvail = sum;
or linq way :
var sum = Enumerable.Range(1, 25)
.Select(x => (int)item.GetType().GetProperty("Sat" + x).GetValue(item, null))
.Sum();
It's not clear if you're using an ORM or ADO, but assuming ADO, you could use something like:
DataTable dt = new DataTable();
foreach (DataRow row in dt.Rows)
{
foreach (DataColumn column in dt.Columns)
{
var satAvail = row[column];
}
}
I'm not sure I'm clear on your actual requirement, but in general, when working with the Database helper, if you want to access a column value resulting from a Database.Query or Database.QuerySingle call, you can either do it using dot notation or an indexer.
For example, you may get data doing this:
var db = Database.Open("MyDatabase");
var item = db.QuerySingle("SELECT * FROM Mytable WHERE ID = 1");
If you know want to access the value of a column called Sat1, you would use item.Sat1. However, if the column name is represented as a variable, you would need to use an indexer instead:
var satWeek = "Sat" + "1";
var satAvail = item[satWeek];

Categories

Resources