The DBConcurrencyException issue occurs when trying to use the Update() method on my Database. I have a table in a database that has an autoincremented ID column, and a DataTable in my c# program that gets its information from this table (including the auto increment part when i use MissingSchemaAction = MissingSchemaAction.AddWithKey).
If I create rows and add them to the datatable, the datatable automatically fills in the autoincremented ID column (starting where the database table left off) for me, which is fine. However if I delete the rows I just added (without first using Update() ) and add new ones, the datatable autoincrement column is filled with a value based on where the DATATABLE is, not where the database is, which is why I get the concurrency error.
for example:
The table in the database has these records:
1 Apple
2 Orange
3 Pear
Which gets copied to the datatable, so when I add a new row with the name value "grape" I get:
1 Apple
2 Orange
3 Pear
4 Grape
Which is fine, however if don't run the Update() method, and I delete the grape row and add a new row "Melon" I get:
1 Apple
2 Orange
3 Pear
5 Melon
And when I try to run Update(), the database is expecting 4 to be the next autoincremented value but instead is getting 5. So I get the error. The Update() occurs when the user clicks a "save" button, so ideally I'd like them to be able to make lots of changes as shown above before finally saving, but is the only way to preserve the concurrency to use Update() after each row is added/deleted?
The expected value is 5 - it would be insanely inefficient for the database to try and fill in the holes in the column every time you do something. Once an auto_increment is used it is gone forever.
Because of this always make sure your column is big enough hold all your records. If you used TINYINT for example then you can only have a 127 record in your table.
The auto increment is stored at the table level and Mysql never looks back to see if it could be lower. You can manually change it by doing the following:
ALTER TABLE tablename AUTO_INCREMENT=2;
But if you do this and there is a collision down the road - bad things are going to happen.
Or you can inspect what it is
SHOW CREATE TABLE tablename;
CREATE TABLE `tablename` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`cat_id` int(10) unsigned NOT NULL,
`status` int(10) unsigned NOT NULL,
`date_added` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `categories_list_INX` (`cat_id`,`status`),
KEY `cat_list_INX` (`date_added`,`cat_id`,`status`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1
And you find out what the last one is.
SELECT LAST_INSERT_ID();
+------------------+
| LAST_INSERT_ID() |
+------------------+
| 2 |
+------------------+
1 row in set (0.00 sec)
My first thought would have been that you should just handle the case that a row is being deleted and first update it, then delete it to keep the auto-increment IDs in sync.
However, I have run into this same situation and it seems to be caused by the 3rd party control my DataGridView is hosted in. Specifically, the problem occurs when the user has focus in the "new" row of the DataGridView, switches to another application and then clicks back into the DataGridView. At this point the original DataRow instance for the new row is deleted and a new one is created with an incremented ID value. I have not been able to figure out a way to handle the deletion of the row before it is actually deleted, nor can I figure out what the 3rd party control is doing that triggers this.
Therefore, for the moment I am handling this problem in a very heavy-handed way, by querying the correct auto-increment value from the database an correcting new DataRows if necessary. If all else fails, this solution seems to work. (Note I am using SqlCe instead of MySQL)
void OnLoad()
{
base.OnLoad(e);
...
_dataTable.TableNewRow += HandleTableNewRow;
}
void HandleTableNewRow(object sender, DataTableNewRowEventArgs e)
{
SetAutoIncrementValues(e.Row);
}
void SetAutoIncrementValues(DataRow row)
{
foreach (DataColumn dataColumn in _dataTable.Columns
.OfType<DataColumn>()
.Where(column => column.AutoIncrement))
{
using (SqlCeCommand sqlcmd = new SqlCeCommand(
"SELECT AUTOINC_NEXT FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '" +
Name + "' AND COLUMN_NAME = '" + dataColumn.ColumnName + "'", _connection))
using (SqlCeResultSet queryResult =
sqlcmd.ExecuteResultSet(ResultSetOptions.Scrollable))
{
if (queryResult.ReadFirst())
{
var nextValue = Convert.ChangeType(queryResult.GetValue(0), dataColumn.DataType);
if (!nextValue.Equals(row[dataColumn.Ordinal]))
{
// Since an auto-increment column is going to be read-only, apply
// the new auto-increment value via a separate array variable.
object[] rowData = row.ItemArray;
rowData[dataColumn.Ordinal] = nextValue;
row.ItemArray = rowData;
}
}
}
}
}
Related
In my database, each letter has its own column, but when I add words to the column, it goes to the bottom and Null fields are created
Now so
And I need it this
I have a list of words and the word must fall into the column from which the first letter of the word begins
My Code
private void ConnectData()
{
_path = "URI=file:" + Application.streamingAssetsPath + "/russianWords.db"; //Path to datab
_dbConnection = new SqliteConnection(_path);
_dbConnection.Open();
if (_dbConnection.State == ConnectionState.Open)
{
m_sqlCmd = new SqliteCommand();
m_sqlCmd.Connection = _dbConnection;
m_sqlCmd.CommandText = "CREATE TABLE IF NOT EXISTS words (а Text , б Text ,в Text,г Text,д Text,е Text,ё Text,ж Text,з Text,и Text,й Text,к Text,л Text,м Text,н Text,о Text,п Text,р Text,с Text,т Text,у Text,ф Text,х Text,ц Text,ч Text,ш Text,щ Text,ъ Text,ы Text,ь Text,э Text,ю Text,я Text)";
m_sqlCmd.ExecuteNonQuery();
}
AddToData("sddd", "т");
}
public void AddToData(string word)
{
try
{
m_sqlCmd.CommandText = $#"INSERT INTO words ('{word.ToCharArray()[0].ToString()}') values ('{ word }')";
m_sqlCmd.ExecuteNonQuery();
}
catch (System.Exception e)
{
Debug.Log(e);
}
}
Databases don't work like this. Putting data in a table will always make a new row. It is not like a game of upwards- Tetris where you insert "Fred" into column "F" and it will find the first empty slot (nearest the "top") and put it there. There isn't even the sense of "top" - datatable tables are just a bunch of rows stored in whatever order the database feels like. If you want your data to have an order you must insert some data that has an order (like an int) and sort by it
If you want this "Tetris" style behavior you must program it explicitly. Have a column that is incrementing numbers
ID A B C
1
2
You want to insert Apple. Select the lowest ID where the column is blank
SELECT MIN(id) FROM t WHERE A IS NULL
ExecuteScalar that and cast the result to an int?
If there is an ID, update it. Otherwise insert:
UPDATE t SET A = 'Apple' WHERE ID = 1
ID A B C
1 Apple
2
Now we want to insert Aeroplane. It will go in ID 2 by the same logic
ID A B C
1 Apple
2 Aeroplane
Now we insert Aurora. There is no row 3. The SELECT that finds the ID will return no rows (you'll get a null returned from execute scalar)
Run the following insert
INSERT INTO t(ID, A)
SELECT MAX(id)+1, 'Aurora' FROM t
ID A B C
1 Apple
2 Aeroplane
3 Aurora
You now have aurora in ID 3
Continue thus either inserting or updating. You should thus have at least one column that is completely full all the time and the others will fill in as they go..
--
Consider to use any auto numbering facility your chosen db has rather than max+1 (but max+1 would work)
I'm trying to use the update() method, but it is inserting my datatable data into my database without checking if the row exists, so it is inserting duplicate data. It is also not deleting rows that don't exist in datatable. How to resolve this? I want to synchronize my datatable with server table.
private void Form1_Load(object sender, EventArgs e)
{
// TODO: This line of code loads data into the 'MyDatabaseDataSet11.Vendor_GUI_Test_Data' table. You can move, or remove it, as needed.
this.vendor_GUI_Test_DataTableAdapter.Fill(this.MyDatabaseDataSet11.Vendor_GUI_Test_Data);
// read target table on SQL Server and store in a tabledata var
this.ServerDataTable = this.MyDatabaseDataSet11.Vendor_GUI_Test_Data;
}
Insertion
private void convertGUIToTableFormat()
{
ServerDataTable.Rows.Clear();
// loop through GUIDataTable rows
for (int i = 0; i < GUIDataTable.Rows.Count; i++)
{
String guiKEY = (String)GUIDataTable.Rows[i][0] + "," + (String)GUIDataTable.Rows[i][8] + "," + (String)GUIDataTable.Rows[i][9];
//Console.WriteLine("guiKey: " + guiKEY);
// loop through every DOW value, make a new row for every true
for(int d = 1; d < 8; d++)
{
if ((bool)GUIDataTable.Rows[i][d] == true)
{
DataRow toInsert = ServerDataTable.NewRow();
toInsert[0] = GUIDataTable.Rows[i][0];
toInsert[1] = d + "";
toInsert[2] = GUIDataTable.Rows[i][8];
toInsert[3] = GUIDataTable.Rows[i][9];
ServerDataTable.Rows.InsertAt(toInsert, 0);
//printDataRow(toInsert);
//Console.WriteLine("---------------");
}
}
}
Trying to update
// I got this adapter from datagridview, casting my datatable to their format
CSharpFirstGUIWinForms.MyDatabaseDataSet1.Vendor_GUI_Test_DataDataTable DT = (CSharpFirstGUIWinForms.MyDatabaseDataSet1.Vendor_GUI_Test_DataDataTable)ServerDataTable;
DT.PrimaryKey = new DataColumn[] { DT.Columns["Vendor"], DT.Columns["DOW"], DT.Columns["LeadTime"], DT.Columns["DemandPeriod"] };
this.vendor_GUI_Test_DataTableAdapter.Update(DT);
Let's look at what happens in the code posted.
First this line:
this.ServerDataTable = this.MyDatabaseDataSet11.Vendor_GUI_Test_Data;
This is not a copy, but just an assignment between two variables. The assigned one (ServerDataTable) receives the 'reference' to the memory area where the data coming from the database has been stored. So these two variables 'point' to the same memory area. Whatever you do with one affects what the other sees.
Now look at this line:
ServerDataTable.Rows.Clear();
Uh! Why? You are clearing the memory area where the data loaded from the database were. Now the Datatable is empty and no records (DataRow) are present there.
Let's look at what happen inside the loop
DataRow toInsert = ServerDataTable.NewRow();
A new DataRow has been created, now every DataRow has a property called RowState and when you create a new row this property has the default value of DataRowState.Detached, but when you add the row inside the DataRow collection with
ServerDataTable.Rows.InsertAt(toInsert, 0);
then the DataRow.RowState property becomes DataRowState.Added.
At this point the missing information is how a TableAdapter behaves when you call Update. The adapter needs to build the appropriate INSERT/UPDATE/DELETE sql command to update the database. And what is the information used to choose the proper sql command? Indeed, it looks at the RowState property and it sees that all your rows are in the Added state. So it chooses the INSERT command for your table and barring any duplicate key violation you will end in your table with duplicate records.
What should you do to resolve the problem? Well the first thing is to remove the line that clears the memory from the data loaded, then, instead of calling always InsertAt you should first look if you have already the row in memory. You could do this using the DataTable.Select method. This method requires a string like it is a WHERE statement and you should use some value for the primarykey of your table
var rows = ServerDataTable.Select("PrimaryKeyFieldName = " + valueToSearchFor);
if you get a rows count bigger than zero then you can use the first row returned and update the existing values with your changes, if there is no row matching the condition then you can use the InsertAt like you are doing it now.
You're trying too hard, I think, and you're unfortunately getting nearly everything wrong
// read target table on SQL Server and store in a tabledata var
this.ServerDataTable = this.MyDatabaseDataSet11.Vendor_GUI_Test_Data;
No, this line of code doesn't do anything at all with the database, it just assigns an existing datatable to a property called ServerDataTable.
for (int i = 0; i < GUIDataTable.Rows.Count; i++)
It isn't clear if GUIDataTable is strongly or weakly typed, but if it's strong (I.e. it lives in your dataset, or is of a type that is a part of your dataset) you will do yourself massive favors if you do not access it's Rows collection at all. The way to access a strongly typed datatable is as if it were an array
myStronglyTypedTable[2] //yes, third row
myStronglyTypedTable.Rows[2] //no, do not do this- you end up with a base type DataRow that is massively harder to work with
Then we have..
DataRow toInsert = ServerDataTable.NewRow();
Again, don't do this.. you're working with strongly typed datatables. This makes your life easy:
var r = MyDatabaseDataSet11.Vendor_GUI_Test_Data.NewVendor_GUI_Test_DataRow();
Because now you can refer to everything by name and type, not numerical index and object:
r.Total = r.Quantity * r.Price; //yes
toInsert["Ttoal"] = (int)toInsert["Quantity"] * (double)toInsert["Price"]; //no. Messy, hard work, "stringly" typed, casting galore, no intellisense.. The typo was deliberate btw
You can also easily add data to a typed datatable like:
MyPersonDatatable.AddPersonRow("John, "smith", 29, "New York");
Next up..
// I got this adapter from datagridview, casting my datatable to their format
CSharpFirstGUIWinForms.MyDatabaseDataSet1.Vendor_GUI_Test_DataDataTable DT = (CSharpFirstGUIWinForms.MyDatabaseDataSet1.Vendor_GUI_Test_DataDataTable)ServerDataTable;
DT.PrimaryKey = new DataColumn[] { DT.Columns["Vendor"], DT.Columns["DOW"], DT.Columns["LeadTime"], DT.Columns["DemandPeriod"] };
this.vendor_GUI_Test_DataTableAdapter.Update(DT);
Need to straighten out the concepts and terminology in your mind here.. that is not an adapter, it didn't come from a datagridview, grid views never provide adapters, your datatable variable was always their format and if you typed it as DataTable ServerDataTable then that just makes it massively harder to work with, in the same way that saying object o = new Person() - now you have to cast o every time you want to do nearly anything Person specific with it. You could always declare all your variables in every program, as type object.. but you don't.. Hence don't do the equivalent by putting your strongly typed datatables inside DataTable typed variables because you're just hiding away the very things that make them useful and easy to work with
If you download rows from a database into a datatable, and you want to...
... delete them from the db, then call Delete on them in the datatable
... update them in the db, then set new values on the existing rows in the datatable
... insert more rows into the db alongside the existing rows, then add more rows to the datatable
Datatables track what you do to their rows. If you clear a datatable it doesn't mark every row as deleted, it just jettisons the rows. No db side rows will be affected. If you delete rows then they gain a rowstate of deleted and a delete query will fire when you call adapter.Update
Modify rows to cause an update to fire. Add new rows for insert
As Steve noted, you jettisoned all the rows, added new ones, added (probably uselessly) a primary key(the strongly typed table will likely have already had this key) which doesn't mean that the new rows are automatically associated to the old/doesn't cause them to be updated, hen inserted a load of new rows and wrote them to the db. This process was never going to update or delete anything
The way this is supposed to work is, you download rows, you see them in the grid, you add some, you change some, you delete some, you hit the save button. Behind the scenes the grid just poked some new rows into the datatable, marked some as deleted, changed others. It didn't go to the huge (and unfortunately incorrect) lengths your code went to. If you want your code to behave the same you follow the same idea:
var pta = new PersonTableAdapter();
var pdt = pta.GetData(); //query that returns all rows
pta.Fill(somedataset.Person); //or can do this
pdt = somedataset.Person; //alias of Person table
var p = pdt.FindByPersonId(123); //PersonId is the primary key in the datatable
p.Delete(); //mark person 123 as deleted
p = pdt.First(r => r.Name = "Joe"); //LINQ just works on strongly typed datatables, out of the box, no messing
p.Name = "John"; //modify joes name to John
pdt.AddPersonRow("Jane", 22);
pta.Update(pdt); //saves changes(delete 123, rename joe, add Jane) to db
What you need to appreciate is that all these commands are just finding or creating datarow obj3cts, that live inside a table.. the table tracks what you do and the adapter uses appropriate sql to send changes to the db.. if you wanted to mark all rows in a datatable as deleted you can visit each of them and call Delete() on it, then update the datatable to save the changes to the db
I need to autoincrement NroFattura and AnnoFattura (this one is the year of the report):
I dont know how to do it, if by C# or by Access settings.
I want that NroFattura is like n = n + 1
And AnnoFattura is like n = get year now (function)
I have a winform behind it (C#)
If you have a simple scenario, where only one user at time works with the database then it is simply a matter to find the highest value inserted for the FatturaNro in a specific year
string query = "SELECT MAX(FatturaNro) FROM yourTableName WHERE AnnoFattura=?";
OleDbCommand cmd = new OleDbCommand(query, connection);
cmd.Parameters.AddWithValue("#p1", DateTime.Today.Year);
object result = cmd.ExecuteScalar();
int newInvoiceNumber = (result == null ? 1 : Convert.ToInt32(result)+1);
The Access auto increment setting is not suitable for this kind of action. If you delete items, the counting will still countinue. For example, if you had 20 items, removed 2, the count would still move to 21 on the next insert. The auto increment field should be used as an unique id of the record, not to as "row number".
You could add a new column for unique indexes, so row specific editing is possible.
Calculating the NroFattura in C# code is easy. Shape the final insert string with the right values and run it.
I created a table in a database with identity column called id . when i delete data from this table and add new data the id column is still increased by 1 , i need to make this column start increasing from the last id inside this table not the deleted one what should i do ?
this is the datagridview with some records:
this is the delete code
if (comboBoxdelete.SelectedIndex != -1)
{
SqlConnection con = new SqlConnection();
SqlCommand cmd = new SqlCommand();
con.ConnectionString = "Data Source=MOHAMEDSAFWAT;Initial Catalog=Card;Integrated Security=True";
cmd.CommandText = "DELETE FROM [Employee] WHERE [int]=#id1";
cmd.Connection = con;
cmd.Parameters.AddWithValue("#id1", comboBoxdelete.SelectedItem);
con.Open();
cmd.ExecuteNonQuery();
con.Close();
load();
View();
}
else
{
MessageBox.Show(" Please Insert ID First");
}
Identity column (in SQL-Sever) is handled by by RDBMS. It will be increased any time you add some row to the table, when removing rows the RDBMS will not rollback the underlying sequence of the identity column.
Id or primary key of your tables are for internal usage of your application. normaly you must not show them to the user.
do not use the id column as row number for end user (as any thing is showing to the user will be compromizer to be requested to be updated or deleted)
By the way lets think we need to implement the id column manually to full fill your requiment.
We can use a simple approach: When adding any new row first we call a select to fetch the new id:
SELECT ISNULL(count(*),0)+ 1 as newid from Employee
We will have the new id.
know the delete scenario, think that we have
Employee1-->id = 1
Employee2-->id = 2
Employee3-->id = 3
Employee4-->id = 4
Now we delete Employee2 so what is needed to be done?
We need updates(after detecting rows to be updated) like this, to achieve your requirement:
Update Employee3 set id = 2
Update Employee4 set id = 3
For all rows in the table!
It will not work in large amount of data, id is the primary key when updating, all child records must also be updated, re-indexing, table locking and ... will be inevitable.
All this is because the id column is being showed to the user and you need that to be shown sequentially.
Use another non key column like Employee-Number or a UI level column like RowNumber for these scenario and do not show your primary key to the user.
I have come across a problem in using the DataAdapter, which I hope someone can help with. Basically I am creating a system, which is as follows:
Data is read in from a data source (MS-Access, SQL Server or Excel), converted to data tables and inserted into a local SQL Server database, using DataAdapters. This bit works fine. The SQL server table has a PK, which is an identity field with auto increment set to on.
Subsequent data loads read in the data from the source and compare it to what we already have. If the record is missing then it is added (this works fine). If the record is different then it needs to be updated (this doesn't work).
When doing the differential data load I create a data table which reads in the schema from the destination table (SQL server) and ensures it has the same columns etc.
The PK in the destination table is column 0, so when a record is inserted all of the values from column 1 onwards are set (as mentioned this works perfectly.). I don't change the row status for items I am adding. The PK in the data table is set correctly and I can confirm this.
When updating data I set column 0 (the PK column) to be the value of the record I am updating and set all of the columns to be the same as the source data.
For updated records I call AcceptChanges and SetModified on the row to ensure (I thought) that the application calls the correct method.
The DataAdapter is set with SelectCommand and UpdateCommand using the command builder.
When I run, I have traced it using SQL profiler and can see that the insert command is being ran correctly, but the update command isn't being ran at all, which is the crux of the problem. For reference an insert table will look something like the following
PK Value1 Value 2 Row State
== ====== ======= =========
124 Test1 Test 2 Added
123 Test3 Test4 Updated
Couple of things to be aware of....
I have tested this by loading the row to be changed into the datatable, changing some column fields and running update and this works. However, this is impractical for my solution because the data is HUGE >1Gb so I can't simply load it into a datatable without taking a huge performance hit. What I am doing is creating the data table with a max of 500 rows and the running the Update. Testing during the initial data load showed this to be the most efficient in terms of memory useage and performance. The data table is cleared after each batch is ran.
Anyone any ideas on where I am going wrong here?
Thanks in advance
Andrew
==========Update==============
Following is the code to create the insert/update rows
private static void AddNewRecordToDataTable(DbDataReader pReader, ref DataTable pUpdateDataTable)
{
// create a new row in the table
DataRow pUpdateRow = pUpdateDataTable.NewRow();
// loop through each item in the data reader - setting all the columns apart from the PK
for (int addCount = 0; addCount < pReader.FieldCount; addCount++)
{
pUpdateRow[addCount + 1] = pReader[addCount];
}
// add the row to the update table
pUpdateDataTable.Rows.Add(pUpdateRow);
}
private static void AddUpdateRecordToDataTable(DbDataReader pReader, int pKeyValue,
ref DataTable pUpdateDataTable)
{
DataRow pUpdateRow = pUpdateDataTable.NewRow();
// set the first column (PK) to the value passed in
pUpdateRow[0] = pKeyValue;
// loop for each row apart from the PK row
for (int addCount = 0; addCount < pReader.FieldCount; addCount++)
{
pUpdateRow[addCount + 1] = pReader[addCount];
}
// add the row to the table and then update it
pUpdateDataTable.Rows.Add(pUpdateRow);
pUpdateRow.AcceptChanges();
pUpdateRow.SetModified();
}
The following code is used to actually do the update:
updateAdapter.Fill(UpdateTable);
updateAdapter.Update(UpdateTable);
UpdateTable.AcceptChanges();
The following is used to create the data table to ensure it has the same fields/data types as the source data
private static DataTable CreateDataTable(DbDataReader pReader)
{
DataTable schemaTable = pReader.GetSchemaTable();
DataTable resultTable = new DataTable(<tableName>); // edited out personal info
// loop for each row in the schema table
try
{
foreach (DataRow dataRow in schemaTable.Rows)
{
// create a new DataColumn object and set values depending
// on the current DataRows values
DataColumn dataColumn = new DataColumn();
dataColumn.ColumnName = dataRow["ColumnName"].ToString();
dataColumn.DataType = Type.GetType(dataRow["DataType"].ToString());
dataColumn.ReadOnly = (bool)dataRow["IsReadOnly"];
dataColumn.AutoIncrement = (bool)dataRow["IsAutoIncrement"];
dataColumn.Unique = (bool)dataRow["IsUnique"];
resultTable.Columns.Add(dataColumn);
}
}
catch (Exception ex)
{
message = "Unable to create data table " + ex.Message;
throw new Exception(message, ex);
}
return resultTable;
}
In case anyone is interested I did manage to get around the problem, but never managed to get the data adapter to work. Basically what I did was as follows:
Create a list of objects with an index and a list of field values as members
Read in the rows that have changed and store the values from the source data (i.e. the values that will overwrite the current ones in the object). In addition I create a comma separated list of the indexes
When I am finished I use the comma separated list in a sql IN statement to return the rows and load them into my data adapter
For each one I run a LINQ query against the index and extract the new values, updating the data set. This sets the row status to modified
I then run the update and the rows are updated correctly.
This isn't the quickest or neatest solution, but it does work and allows me to run the changes in batches.
Thanks
Andrew