Adding a Row to a DataTable question - c#

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);

Related

When adding a row to DataGridView, a new blank row is added and data is added to the end of the current row

I'm trying to add data manually to a DataGridView (displaying a grid view of a student's attendance for the year). The problem is, when I add a row of data to the DataGridView instead of a new row being created and the data being added to it. A new blank row is made and the data is added to the top row. Here is the relevant code:
foreach (IndividualAttendanceRecord rec in DatabaseInterfacer.GetRecords("pi404"))
{
if (dataGrid.ColumnCount < rec.Attendance.Count)
dataGrid.ColumnCount = rec.Attendance.Count;
List<String> row = new List<string>();
foreach (string entry in rec.Attendance)
row.Add(entry);
string[] rowArray = row.ToArray<string>();
dataGrid.Rows.Add(rowArray);
}
Doing this code makes a DataGridView with all the data in one line, then two blank lines at the bottom.
Any help?
EDIT:
Still completely stumped on this. I've simplified my code and added a few test rows to the foreach statement and I don't understand why it's outputting the way it is at all. Here is my new code:
foreach (IndividualAttendanceRecord rec in DatabaseInterfacer.GetRecords("pi404"))
{
if (dataGrid.ColumnCount < rec.Attendance.Count)
dataGrid.ColumnCount = rec.Attendance.Count;
string[] row = rec.Attendance.ToArray<string>();
dataGrid.Rows.Add(row);
dataGrid.Rows.Add("1", "2", "3");
dataGrid.Rows.Add("One", "Two", "Three");
}
And here is what it outputs: http://i.imgur.com/f45mlod.png
I don't see why it is still putting all the information in the IndividualAttendanceRecord in a single line on it's own, and then creating a blank line and puting the "1 2 3" and "one two three".
Can anyone see why this is happening? I'm probably being really stupid.
The control is showing what you said to show:
First you said to grid to create some columns by setting ColumnCount to the count of items of your list:
dataGrid.ColumnCount = rec.Attendance.Count;
Then you add a row containing some values using Add( params object[] values) method. when you pass an array to the method, it will adds a row and use those values as columns:
string[] rowArray = row.ToArray<string>();
dataGrid.Rows.Add(rowArray);
If you want to added all values in a single column, as an option you can:
dataGrid.ColumnCount = 1;
foreach (string entry in rec.Attendance)
dataGrid.Rows.Add(entry);
I looked through the rest of my code and found the problem. There was no problem with the display code, the problem was actually in the database. For some reason all the data was actually in one line of the database with two blank lines underneath.

Targeting a specific column in a DataRow

I'm trying to perform the C# equivalent of Select * where [columnname] = [value]. I began with a foreach loop to iterate through the table row by row, however I had forgotten that one cannot access a column via row.column["<colname>"].
How do I achieve this objective? Most of the examples I have seen target one specific row with the intention of casting it's value to a string, however my task is to move all entries with a value of DateTime == < DateTime.Today to an archived table.
Can I continue with the following code? Or am I approaching this in the wrong manner?
void archiveDates()
{
foreach (DataRow row in workingupdates.storageTable.Rows)
{
//target DateTime column here
}
}
You can use the Field extension method that is strongly typed and also supports nullable types. You have an overload for the index, name or the DataColumn(among others):
foreach (DataRow row in workingupdates.storageTable.Rows)
{
DateTime dt = row.Field<DateTime>("columnname");
}
If you instead want to find all rows where the date column has a specific value you can use Linq-To-DataTable:
var matchingDataRows = workingupdates.storageTable.AsEnumerable()
.Where(row => row.Field<DateTime>("columnname") == dateTimeVariable);
Now you can simply enumerate this query:
foreach (DataRow row in matchingDataRows)
{
// ...
}
Or create a collection like
a DataRow[] with matchingDataRows.ToArray() or
a List<DataRow> with matchingDataRows.ToList()
a new DataTable with matchingDataRows.CopyToDataTable()
Note that you have to add System.Linq; to the top of the file.

Fill DataTable using array of NewRows

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.

Retrieve "row pairs" from Excel

I am trying to retrieve data from an Excel spreadsheet using C#. The data in the spreadsheet has the following characteristics:
no column names are assigned
the rows can have varying column lengths
some rows are metadata, and these rows label the content of the columns in the next row
Therefore, the objects I need to construct will always have their name in the very first column, and its parameters are contained in the next columns. It is important that the parameter names are retrieved from the row above. An example:
row1|---------|FirstName|Surname|
row2|---Person|Bob------|Bloggs-|
row3|---------|---------|-------|
row4|---------|Make-----|Model--|
row5|------Car|Toyota---|Prius--|
So unfortunately the data is heterogeneous, and the only way to determine what rows "belong together" is to check whether the first column in the row is empty. If it is, then read all data in the row, and check which parameter names apply by checking the row above.
At first I thought the straightforward approach would be to simply loop through
1) the dataset containing all sheets, then
2) the datatables (i.e. sheets) and
3) the row.
However, I found that trying to extract this data with nested loops and if statements results in horrible, unreadable and inflexible code.
Is there a way to do this in LINQ ? I had a look at this article to start by filtering the empty rows between data but didn't really get anywhere. Could someone point me in the right direction with a few code snippets please ?
Thanks in advance !
hiro
I see that you've already accepted the answer, but I think that more generic solution is possible - using reflection.
Let say you got your data as a List<string[]> where each element in the list is an array of string with all cells from corresponding row.
List<string[]> data;
data = LoadData();
var results = new List<object>();
string[] headerRow;
var en = data.GetEnumerator();
while(en.MoveNext())
{
var row = en.Current;
if(string.IsNullOrEmpty(row[0]))
{
headerRow = row.Skip(1).ToArray();
}
else
{
Type objType = Type.GetType(row[0]);
object newItem = Activator.CreateInstance(objType);
for(int i = 0; i < headerRow.Length; i++)
{
objType.GetProperty(headerRow[i]).SetValue(newItem, row[i+1]);
}
results.Add(newItem);
}
}

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

Categories