Can't add more than one row to DataSet - c#

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.

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

Insert All only inserts last record? LINQ to SQL

No matter what I try I cannot get this to work.
It is a similar question to this one InsertAllOnSubmit only inserts first data record where in my case it only inserts the last record set.
At first I though it was the primary key was not being set as the manual setting using DateTime.UtcNow.Ticks to update the ID column. But after changing the setting in SQL server I was able to manually set the primary key.
I though this would fix the issue, but now it updates the primary key ,but only inserts the last record in the foreach loop.
I tried deleting the Dbml and recreating it, but that did nothing.
This is the code I am using,
List<Tenant_Bills_TBL> addNewData = new List<Tenant_Bills_TBL>();
Tenant_Bills_TBL addBill = new Tenant_Bills_TBL();
var recordsForTenant = Database.GetBillsRecordsForTenant(Database.DataContext, SelectedTenant.Code);
foreach (var item in recordsForTenant)
{
addBill.ID = DateTime.UtcNow.Ticks;
addBill.Tenant_Code = SelectedTenant.Code;
addBill.Year_Data = DateFilter.Year;
addBill.Month_Data = DateFilter.Month;
addBill.Tenant_Bill = item.Tenant_Bill;
addBill.Bill_Amount = item.Bill_Amount;
addBill.Bill_Quantity = item.Bill_Quantity;
addNewData.Add(addBill);
}
Database.DataContext.Tenant_Bills_TBLs.InsertAllOnSubmit(addNewData);
Database.DataContext.SubmitChanges();
It is like the list is overwriting the values after being added only leaving the last dataset in the List<T>
Is there a setting on the server side that I need to change or is it something else?
The row
Tenant_Bills_TBL addBill = new Tenant_Bills_TBL();
should be inside the foreach loop.
The code in the question instantiate a single object and override it's properties with every iteration.
Also, having DateTime.UtcNow.Ticks as a primary key is not really recommended. You better use the database auto-increment buit in mechanism (most databases have that option). As you found out yourself, a foreach loop is simply too fast for the ticks to change between each iteration, and if you are even thinking of multy threaded inserts you can clearly see that even having Thread.Sleep inside the loop is not good enough.
You are getting this behavior because you have the same object added over and over. As the comment said - you should move the instantiation of a new object inside the loop.
Even better - you can make it a LINQ statement:
var newData = Database
.GetBillsRecordsForTenant(Database.DataContext, SelectedTenant.Code)
.Select(item => new Tenant_Bills_TBL{
ID = DateTime.UtcNow.Ticks;
Tenant_Code = SelectedTenant.Code;
Year_Data = DateFilter.Year;
Month_Data = DateFilter.Month;
Tenant_Bill = item.Tenant_Bill;
Bill_Amount = item.Bill_Amount;
Bill_Quantity = item.Bill_Quantity;
})
.ToList();
Database.DataContext.Tenant_Bills_TBLs.InsertAllOnSubmit(addNewData);

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.

How to read from DataGrid Column Cells?

I'm messing with this problem for about 2 days now and searched on many boards for a solution to solve the problem :(
I wrote via linq XML Attributes in my DataGrids Column named "Betrag".
Now I want to get all of those Entries and then sum them up to one number ( all entries of the column are numbers!).
I hope somebody can help me with this problem.
Best Regards,
Fabian
Now some code :
data = new List<Daten>();
data = (from datensatz in doc1.Descendants("datensatz")
select new Daten
{
//datum = "27.6.2012",
datum =datensatz.Attribute("datum").Value,
//zweck = "Eröffnung",
zweck =datensatz.Attribute("zweck").Value,
//empfang = benutzer,
empfang =datensatz.Attribute("empfang").Value,
//betrag = "0€"
betrag =datensatz.Attribute("betrag").Value + "€"
}).ToList();
this.Daten.ItemsSource = data;
//THIS CODE ADDS THE ATTRIBUTES TO MY GRID
then I tried this :
kontostand += Convert.ToInt32(Daten.Columns[3].GetCellContent(1).ToString());
Why not just do something like this...
var sum = data.Sum(item=>item.betrag);//you might have to parse as number.
you could put that value in a property on the page and then put a databinding expression wherever you want to display the value.
I think you should avoid trying to sum the values in the cells.
Also, I think you should make the betrag property an integer, if possible. You could always add the symbol by using String.Format on the code in front.
This :
kontostand += Convert.ToInt32(Daten.Columns[3].GetCellContent(1).ToString());
Should be like this if its an asp grid:
kontostand += Convert.ToInt32(Daten.Rows.Cells[3].innerText);
If not then you need to loop the rows.

How to add a row to a unbound DataGridView?

I have a DataGridView in C# and I want to add rows in a programmatic way. There is no data bound to the grid but when I call dataGrid.Rows.Add(); it throws a System.InvalidOperationException.
I looked all over the internet and I only found this problem for people who have data bound to it. I want the grid to be controlled completely from the code.
Could anyone help me with this please?
Not sure if it makes a difference but I use .Net framework 3.5.
Assuming you have created the columns, either with the designer or by code you can do:
var row = (DataGridViewRow)myDataGridView.RowTemplate.Clone();
row.CreateCells(myDataGridView, "I'm Cell 1", "I'm Cell 2", "etc.");
myDataGridView.Rows.Add(row);
Ideally if you are adding many rows you would create an array of rows upfront and call AddRange(rows); instead.
Example:
void PopulateGrid()
{
//Consider Suspend-Resume Layout, YMMV.
var rows = myData.Select(data => CreateRow(data)).ToArray();
myDataGridView.Rows.AddRange(rows);
}
DataGridViewRow CreateRow(MyData data)
{
var row = (DataGridViewRow)myDataGridView.RowTemplate.Clone();
row.CreateCells(myDataGridView, data.Text, data.Date, date.Value);
return row;
}
the easiest example i could give is:
/// <summary>
/// Shows example usage of Add method on Rows.
/// </summary>
void M()
{
//
// n is the new index. The cells must also be accessed by an index.
// In this example, there are four cells in each row.
//
int n = dataGridView1.Rows.Add();
dataGridView1.Rows[n].Cells[0].Value = title;
dataGridView1.Rows[n].Cells[1].Value = dateTimeNow;
//
// The second cell is a date cell, use typeof(DateTime).
//
dataGridView1.Rows[n].Cells[1].ValueType = typeof(DateTime);
dataGridView1.Rows[n].Cells[2].Value = wordCount;
}
I usually go with answers provided by other people but this is not the case since the answers aren't really helpful.
As I stated "dataGridView1.Rows.Add();" threw an exception and so did AddRange.
I found out the answer after doing a lot of checks. Apparently .Net does not like it if I add a lot of rows/second (about 30).
I receive my rows via networking so I created a pool of rows and every second I updated the rows from the datagridview.
This seems to have fixed both the rows not showing up and the exceptions.
Thank you for the input anyway!

Categories