Fill DataTable using array of NewRows - c#

and thanks for the help! I'm trying to fill a DataTable before its pulled in a Report from Microsoft Reporting Services.
I originally thought I could run a for look (depending on how many rows there are in my "MainTable", and assign each category and flush it into a Rows.Add and repeat but apparently I cannot Row.Add the same name NewRow. Here's what I have thus far. thanks for the help!:
MyDataSet.ESSRow newESS = MyDataSet.ESS.NewESSRow();
for (int i = 0; i < ds.Tables["MainTable"].Rows.Count; i++)
{
DataRow dRow = ds.Tables["MainTable"].Rows[i];
if(Convert.ToInt32(dRow.ItemArray.GetValue(9).ToString()) > ShiftDelta)//checks if instance is longer than a shift
{
newESS.Station = "7";
newESS.Switch ="7";
newESS.Start = dRow.ItemArray.GetValue(6).ToString();
newESS.Stop = dRow.ItemArray.GetValue(7).ToString();
newESS.SwitchIs = dRow.ItemArray.GetValue(8).ToString();
TimeSpan t = TimeSpan.FromSeconds(Convert.ToInt32(dRow.ItemArray.GetValue(9).ToString()));
newESS.Duration = string.Format("{0:D2}h:{1:D2}m:{2:D2}s", t.Hours, t.Minutes, t.Seconds);
MyDataSet.ESS.Rows.Add(newESS); }
}

Also try to rather use importRow as this does not give the annoying row already belongs to another table error.

Don't try to add the same row several times, instead create a new row to add in each iteration of the loop. This can be done most simply by just moving the declaration of newESS to be inside of the loop rather than outside of the loop. Beyond that, you should also move it to be inside of the if statement so that you aren't creating a new row if you aren't going to add one.

Related

DataRow.SetField() gives a null ref exception when adding data to a column I previously deleted then added back

UPDATE
I think I have found what is causing the issue here https://stackoverflow.com/a/5665600/19393524
I believe my issue lies with my use of .DefaultView. The post thinks when you do a sort on it it is technically a write operation to the DataTable object and might not propagate changes made properly or entirely. It is an interesting read and seems to answer my question of why passing valid data to a DataRow is throwing this exception AFTER I make changes to the datatable
UPDATE:
Let me be crystal clear. I have already solved my problem. I would just like to know why it is throwing an error. In my view the code should work and it does.. the first run through.
AFTER I have already deleted the column then added it back (run this code once)
When I debug my code line by line in Visiual studio and stop at the line:
data.Rows[i].SetField(sortColumnNames[k], value);
the row exists
the column exisits
value is not null
sortColumnNames[k] is not null and contains the correct column name
i is 0
Yet it still throws an exception. I would like to know why. What am I missing?
Sorry for the long explanation but this one needs some context unfortunately.
So my problem is this, I have code that sorts data in a DataTable object by column. The user picks the column they want to sort by and then my code sorts it.
I ran into an issue where I needed numbers to sort as numbers not strings (all data in the table is strings). eg (string sorting would result in 1000 coming before 500)
So my solution was to create a temporary column that uses the correct datatype so that numbers get sorted properly and the original string data of the number remains unchanged but is now sorted properly. This worked perfectly. I could sort string numeric data as numeric data without changing the formatting of the number or data type.
I delete the column I used to sort afterwards because I use defaultview to sort and copy data to another DataTable object.
That part all works fine the first time.
The issue is when the user needs to do a different sort on the same column. My code adds back the column. (same name) then tries to add values to the column but then I get a null reference exception "Object not set to an instance of an object"
Here is what I've tried:
I've tried using AcceptChanges() after deleting a column but this did nothing.
I've tried using column index, name, and column object returned by DataTable.Columns.Add() in the first parameter of SetField() in case it was somehow referencing the "old" column object I deleted (this is what I think the problem is more than likely)
I've tried changing the value of the .ItemArray[] directly but this does not work even the first time
Here is the code:
This is the how the column names are passed:
private void SortByColumn()
{
if (cbAscDesc.SelectedIndex != -1)//if the user has selected ASC or DESC order
{
//clears the datatable object that stores the sorted defaultview
sortedData.Clear();
//grabs column names the user has selected to sort by and copies them to a string[]
string[] lbItems = new string[lbColumnsToSortBy.Items.Count];
lbColumnsToSortBy.Items.CopyTo(lbItems, 0);
//adds temp columns to data to sort numerical strings properly
string[] itemsToSort = AddSortColumns(lbItems);
//creates parameters for defaultview sort
string columnsToSortBy = String.Join(",", itemsToSort);
string sortDirection = cbAscDesc.SelectedItem.ToString();
data.DefaultView.Sort = columnsToSortBy + " " + sortDirection;
//copies the defaultview to the sorted table object
sortedData = data.DefaultView.ToTable();
RemoveSortColumns(itemsToSort);//removes temp sorting columns
}
}
This is where the temp columns are added:
private string[] AddSortColumns(string[] items)//adds columns to data that will be used to sort
//(ensures numbers are sorted as numbers and strings are sorted as strings)
{
string[] sortColumnNames = new string[items.Length];
for (int k = 0; k < items.Length; k++)
{
int indexOfOrginialColumn = Array.IndexOf(columns, items[k]);
Type datatype = CheckDataType(indexOfOrginialColumn);
if (datatype == typeof(double))
{
sortColumnNames[k] = items[k] + "Sort";
data.Columns.Add(sortColumnNames[k], typeof(double));
for (int i = 0; i < data.Rows.Count; i++)
{
//these three lines add the values in the original column to the column used to sort formated to the proper datatype
NumberStyles styles = NumberStyles.Any;
double value = double.Parse(data.Rows[i].Field<string>(indexOfOrginialColumn), styles);
bool test = data.Columns.Contains("QtySort");
data.Rows[i].SetField(sortColumnNames[k], value);//this is line that throws a null ref exception
}
}
else
{
sortColumnNames[k] = items[k];
}
}
return sortColumnNames;
}
This is the code that deletes the columns afterward:
private void RemoveSortColumns(string[] columnsToRemove)
{
for (int i = 0; i < columnsToRemove.Length; i++)
{
if (columnsToRemove[i].Contains("Sort"))
{
sortedData.Columns.Remove(columnsToRemove[i]);
}
}
}
NOTE:
I've been able to fix the problem by just keeping the column in data and just deleting the column from sortedData as I use .Clear() on the sorted table which seems to ensure the exception is not thrown.
I would still like an answer though as to why this is throwing an exception. If I use .Contains() on the line right before the one where the exception is thrown is says the column exists and returns true and in case anyone is wondering the params sortColumnNames[k] and value are never null either.
Your problem is probably here:
private void RemoveSortColumns()
{
for (int i = 0; i < data.Columns.Count; i++)
{
if (data.Columns[i].ColumnName.Contains("Sort"))
{
data.Columns.RemoveAt(i);
sortedData.Columns.RemoveAt(i);
}
}
}
If you have 2 columns, and the first one matches the if, you will never look at the second.
This is because it will run:
i = 0
is i < columns.Count which is 2 => yes
is col[0].Contains("sort") true => yes
remove col[0]
i = 1
is i < columns.Count which is 1 => no
The solution is to readjust i after the removal
private void RemoveSortColumns()
{
for (int i = 0; i < data.Columns.Count; i++)
{
if (data.Columns[i].ColumnName.Contains("Sort"))
{
data.Columns.RemoveAt(i);
sortedData.Columns.RemoveAt(i);
i--;//removed 1 element, go back 1
}
}
}
I fixed my original issue by changing a few lines of code in my SortByColumn() method:
private void SortByColumn()
{
if (cbAscDesc.SelectedIndex != -1)//if the user has selected ASC or DESC order
{
//clears the datatable object that stores the sorted defaultview
sortedData.Clear();
//grabs column names the user has selected to sort by and copies them to a string[]
string[] lbItems = new string[lbColumnsToSortBy.Items.Count];
lbColumnsToSortBy.Items.CopyTo(lbItems, 0);
//adds temp columns to data to sort numerical strings properly
string[] itemsToSort = AddSortColumns(lbItems);
//creates parameters for defaultview sort
string columnsToSortBy = String.Join(",", itemsToSort);
string sortDirection = cbAscDesc.SelectedItem.ToString();
DataView userSelectedSort = data.AsDataView();
userSelectedSort.Sort = columnsToSortBy + " " + sortDirection;
//copies the defaultview to the sorted table object
sortedData = userSelectedSort.ToTable();
RemoveSortColumns(itemsToSort);//removes temp sorting columns
}
}
Instead of sorting on data.DefaultView I create a new DataView object and pass data.AsDataView() as it's value then sort on that. Completely gets rid of the issue in my original code. For anyone wondering I still believe it is bug with .DefaultView in the .NET framework that Microsoft will probably never fix. I hope this will help someone with a similar issue in the future.
Here is the link again to where I figured out a solution to my problem.
https://stackoverflow.com/a/5665600

Datagridview - Quicker way to populate with List loop?

Currently, I'm planning to add ~500K List to datagridview.
Class has multiple Fields, Each List has 13 fields.
but i bind only 7 fields for now.
Problem is,it seems like adding takes too much time. like 5sec for 15K which is awful.
Is there any ways to optimze this?
or should i throw away datagridview and consider some other views?
private void UpdateDataGrid()
{
this.dataGridView1.Rows.Clear();
for (int i = 0; i < gVar.gTagCount; i++)
{
this.dataGridView1.Rows.Add(new object[]
{
gVar.Tags[i].TagCount,
gVar.Tags[i].Name,
gVar.Tags[i].Score.Story,
gVar.Tags[i].Score.Drawing,
gVar.Tags[i].Score.Drawing,
gVar.Tags[i].Score.Memetic,
gVar.Tags[i].DupeCount
});
}
}
According to what we discussed my approach would be this:
First make a new Class, I would call it TagsMin this class should contain the 7 things you want in your datagridview.
Secondly i would populate that a list of that class like this (you need to complete it with what you want):
var tagList = gVar.Tags.Select(x => new TagsMin() { TagCount = x.TagCount, Name = x.Name... }).ToList()
And the last step, would be to bind it to the datagridview:
dataGridView1.DataSource = tagList;
Consider using paging so that you are not loading all of the data at once. The answer to the question linked to below provides an example.
How can we do pagination in datagridview in winform
Can you try to avoid the loop and directly bind the list with the standard way:
dataGridView1.DataSource = null;
dataGridView1.DataSource = gVar.Tags;

Can't add more than one row to DataSet

Here's the code:
DataSet1.CashRow CashRow = MainDataSet.Cash.NewCashRow();
CashRow.SetIdNull();
CashRow.Date = CashItem.Date;
CashRow.Description = CashItem.Description;
CashRow.Amount = CashItem.Amount;
MainDataSet.Cash.Rows.Add(CashRow);
It works just fine for the first time. However, if put in a cycle and simply doubled, no more rows are added. I have to close application and start it again.
Update: I have a DataGridView which is connected to the DataSet. Could this cause any problems? This issue is really weird, been trying to fix this for a week now. Maybe there are another ways to add rows?
Update 2: found the cause: "System.Data.ConstraintException: Column 'Id' is constrained to be unique. Value '' is already present.".
try this:
DataSet1.CashRow CashRow = MainDataSet.Cash.NewCashRow();
CashRow.Date = CashItem.Date;
CashRow.Description = CashItem.Description;
CashRow.Amount = CashItem.Amount;
MainDataSet.Cash.AddCashRow(CashRow);
CashTableAdapter.Update(MainDataSet);
And if it still update only one row put this block as function, like this:
private void CreateCashRow (DateTime date, string description, int amount)
{
DataSet1.CashRow CashRow = MainDataSet.Cash.NewCashRow();
CashRow.Date = date;
CashRow.Description = description;
CashRow.Amount = amount;
MainDataSet.Cash.AddCashRow(CashRow);
CashTableAdapter.Update(MainDataSet);
}
And call the function as many time you need.
You must make sure you have PrimaryKey on the table
I also suggest to you not to use so similar names for vars and types like you did with the CashRow.

Creating a dynamic variable

Ive been working on a program that allows me to updload data from my sql server DB into a SOAP service. I can successfully upload the data into the service. The problem is that the same row is being inserted. The following is a part of my code: `
List<service1.SDataRow> ArrayOfSData = new List<ne_service_demo2.service1.SDataRow>();
//data to be uploaded.
service1.SDataRow row1 = new service1.SDataRow();
foreach (DataRow dRow in dTable.Rows)
{
row1.SensorTimeStamp = dRow["SensorTimeStamp"].ToString();
row1.SensorID = dRow["SensorID"].ToString();
row1.PercentFull = dRow["PercentFull"].ToString();
row1.Temp = dRow["Temp"].ToString();
row1.TempReading = dRow["TempReading"].ToString();
row1.Sig = dRow["Sig"].ToString();
row1.SignalReading = dRow["SignalReading"].ToString();
row1.Noi = dRow["Noi"].ToString();
row1.NoiseReading = dRow["NoiseReading"].ToString();
row1.Cou = dRow["Cou"].ToString();
row1.CountReading = dRow["CountReading"].ToString();
row1.NewPercentFull = dRow["NewPercentFull"].ToString();
row1.Current_Gallons_In_Tank = dRow["Current_Gallons_In_Tank"].ToString();
ArrayOfSData.Add(row1);
}
service1.SensorData sd = new service1.SensorData();
sd.API_ACCESS_KEY = "7e070c1981ccc368483a207801be17aa17b2334c";
sd.AccountCode = "0000000";
sd.SData = ArrayOfSData.ToArray();
// Asynchronous call
ws.PostSensorDataCompleted += WebSeviceResult;
ws.PostSensorDataAsync(sd);`
As I loop through my DB table, all data goes to the appropriate fields or columns. Again, the problem is that I get the same row x amount of times. I know the problem is "row1" because it is constantly being updated with new values while still keeping the list count. However, I can not make row1 a list type because it must be of SDataRow type since that is the class that links my service to the columns of my DB. I also tried using the List.Clear() method to clear out my list within my loop but did not work. When I tried clearing the list after every iteration I would get an error saying "Item has already been added." So in theory the clear() method should work, its the row1 that is causing problems. Does anyone know of a work around or a way to make
You are creating an instance of the SDataRow outside the loop. Inside the loop, you are setting the properties of that same instance (overwriting them each iteration), then adding that same instance to the list.
Change that part of the code to this:
foreach (DataRow dRow in dTable.Rows)
{
service1.SDataRow row1 = new service1.SDataRow();
So you need to create new instances inside the foreach loop

Adding a Row to a DataTable question

Greetings everyone-
In my code below I'm trying to add a Row from an existing DataTable (dtResult) into a new DataTable (dtCopyResult) if email address does not match. So I guess my knowledge of ADO.NET is not up to par because whenever I try to run my below code, I get an "This Row already belongs to another table". Please let me know how I can fix this..
Many Thanks
if (checkBox1.Checked)
{
for (int i = dtResult.Rows.Count - 1; i >= 0; i--) //dtResult is a DataTable
{
foreach (object email in emails) //emails is an ArrayList of email addresses
{
if (email.ToString().ToUpper() != dtResult.Rows[i][3].ToString().ToUpper())
{
dtCopyResult.Rows.Add(dtResult.Rows[i]); //dtCopyResult is a new blank DataTable that I'm trying to add rows to
}
}
}
}
As the error message tells you, a DataRow belongs to a particular DataTable; you cannot just take it and add it to another one. What you can do is either
create a new DataRow and fill it with the values from the old DataRow or
use the DataTable.ImportRow method:
dtCopyResult.ImportRow(dtResult.Rows[i]);
You can use ImportRow function, full example here http://support.microsoft.com/kb/308909
One thing I noticed is that the new row will get added multiple times; once for each item in the emails collection.
You either need to keep a local list of items already added or loop through dtCopyResult to make sure you have not already added the email.
List<string> alreadyAdded = new List<string>();
if (email.ToString().ToUpper() != dtResult.Rows[i][0].ToString().ToUpper()
&& !alreadyAdded.Contains(email.ToString()))
{
dtCopyResult.ImportRow(_dt1.Rows[i]);
alreadyAdded.Add(email.ToString());
}
it means the adding row is belong to "dtResult" and DataRow is an "Object" that represent data. Not data itselfs. so in this case u try to add DataRow object that belong to another table which will error.
another way to do is copy everything to dtCopy and delete it if condition mismatch.
Array is mainly to used to stored various type of object. In this case, if u gonna store only email u should use
List<string> emails = new List<string>();
emails.Add("test#example.com");
to enumerate rows for deleteing data
List<DataRow> removing = new List<DataRow>();
foreach(var row in table.AsEnumerable()) // or table.Rows
if(...condition)removing.Add(row);
foreach(var row in removing)table.Rows.Remove(row);
the reason to use 'removing' is if u loop through rows and removing it at the same time, means u change the enumerate which will cause error. becus .Net is not happy when u pull out something its looping.
Try replacing
dtCopyResult.Rows.Add(dtResult.Rows[i]);
with
DataRow rowToBeCopied = dtCopyResult.NewRow();
//Copy the row values..
rowToBeCopied.X = dtResult.Rows[i].X;
//Copy the remaining row values
dtCopyResult.Rows.Add(rowToBeCopied);

Categories