DBConcurrency exception not occuring when data has been updated elsewhere - c#

Situation:
I'm writing a Winforms app using C# in VS2013 with .NET4.0.
I have a datagridview bound to a dataadapter that loads data from a MySQL table. dataadapter SQL command have been built manually as some reformatting is needed to accord with date and boolean handling on the database. This is a multi user environment so data contention is possible. I therefore handle DBConcurrency exceptions when carrying out dataadapter.Update().
For testing the adapter is filled, bound to the dgv and then I use MySQLWorkbench to delete one of the displayed rows. If I then try to update that row in the dgv the DBConcurrency exception is thrown correctly.
Issue:
If however I only amend the row with MySQLWorkbench, then update through the dgv the DBConcurrency exception is not thrown and the update is successful. Hence any update that another user might have made gets lost. (NB I am ensuring that the MySQLWorkbench change is properly committed first!)
Question:
How do I get the DBConcurrency exception thrown when there is amend contention?
EDIT
This is actually a tabbed application with a number of datagridviews each linked to a MySQL table. All have the issue. A really simple example is a "maker" table consisting of:
maker_id int(11) not nullable auto-increment
maker_name varchar(127) not nullable
The SQL is generated through code and populated via parameters. Through the debugger just before datadapter.Update I see:
UPDATE maker SET maker_id = #maker_id, maker_name = #maker_name WHERE maker_id = #maker_id

Related

Effective way of displaying results of stored procedure with interactive button columns

Apologies if the title is vague - I will explain in as much detail as possible the problem.
I have an ASP.NET webpage which queries a TSQL database using a stored procedure, the query is run on Page_Load. The procedure returns a dataset (with ID column, RequestID). The contents are essentially a list of data-specific requests that need to be approved or rejected as exceptions to another workflow (not entirely relevant but may help explain my approach)
I'd like to display the results of this procedure in a grid (currently this is easy enough) however I'm running into problems when trying to add buttons that allow the user to interact with the data, e.g. I'd like 'Reject/Approve' buttons.
When one of these buttons is pressed the relevant stored procedure would be run, with the RequestID of the row pressed passed as a parameter. The idea is to have the stored procedure update an audit log and update the relevant tables accordingly (each request represent a complex data object spanning multiple tables) - for this reason the default controls aren't sufficient for select/edit/delete.
I feel like I'm doing this entirely wrong from the basic approach, which is using a gridview and trying to add custom SelectMethod/DeleteMethod's. What would be a best practice approach for doing this?
EDIT*
Here is the data table and how i'm populating it:
DataTable Requests = new DataTable();
Requests = GetRequests(connString); //Method that returns a datatable from a stored procedure
Displaying the data in a gridview as follows:
gridView.DataSource = Requests;
gridView.DataBind();
However when i add the following code, which is me trying to fire a custom method when using the default select/delete controls in a gridview i get an exception:
gridView.AutoGenerateDeleteButton = true;
gridView.AutoGenerateSelectButton = true;
gridView.SelectMethod = "ApproveRequest";
gridView.DeleteMethod = "RejectRequest";
Here is the exceptiopn (which make sense, but makes me think i'm going about this the wrong way).
Message=DataSource or DataSourceID cannot be defined on 'gridView' when it uses model binding.

Record and Table locking in C# WinForms with PostgreSql and ADO.NET

I am using.NET Framework 4.6.1, WinForms, PostgreSQL 6.4beta4 and Npgsql and ADO.NET.
My current application is a multi-user-application where all users connect to the same database.
Data gets bound to the controls by using DataTable, BindingSource, and BindingNavigator.
I want to avoid that two users can edit a DataRow at the same time.
Because I want to implement this on a more general approach I was thinking about creating a DataTable descendant and add the property LockMode (None, Row, Table).
I found out that you can use the ColumnChanged event in combination with RowState to detect changes on the data.
I now know whether the user is inserting a new value, editing (RowState = modified) an existing one or just looks (RowState = Unchanged) at the record.
Therefore I am looking for a solution to lock the DataRow once a user starts editing it. In the application, i want to display a message once a user navigates (by using the Bindingnavigator or programmatically) to a locked record.
Most solutions I found target MySql Server like this one: How to perform a row lock? or TransactionScope locking table and IsolationLevel.
However I am looking for a PostgreSQL solution, so even articles on this topic from MS (https://msdn.microsoft.com/en-us/library/system.transactions.transactionscope(v=vs.110).aspx) cannot be used here.
I would appreciate if someone with experience in PostgreSQL and ADO.NET could help me out here. Thanks.
You have to sync your clients to achieve that.
I would add an extra nullable date column (RowIsBeeingEdited), indicating the time when the row started beeing edited. I would set the row editable/not on client app start out of row[RowIsBeeingEdited] value.
Also I would implement two signals: {UserStartedEditingRow} & {UserFinishedEditingRow}, that would propagate to all clients, indicating, that client X started/finished editing row Y.
On begin edit row I would set row[RowIsBeeingEdited] = {now} and send {UserStartedEditingRow} signal. On end edit I would set row[RowIsBeeingEdited] = null and send {UserFinishedEditingRow} signal. Currently active clients should receive both signals and set row editable/not.
Hope that has some value.
Suggestion provided by #lokusking sounds good. Easy for maintenance and extend-ability.
You should add a RowVersion or Timestamp typed column in the table, and use that as a concurrency token in the update statement and throw a concurrency exception when the value is changed before submitting user changes.

DataGridView: cannot add row or insert to table

I have a DataGridView. It uses a BindingSource, a DataTable in a DataSet, and a TableAdapter to add/change/delete data in a table. It worked OK, but stopped working when I added a field/column, and I can't figure out what I did or how fix it.
The user can add a new row at the bottom of the DataGridView, but when he goes to save, the row disappears and is not saved. In addition, if he tries to type a second new row, the first new row disappears.
Existing Rows can be changed and saved back to the database successfully.
I've been asked for code. OK, here is code. (I've eliminated some error checking done by scanning dtDep) The point that after the third line is executed, there are no rows in dtDep even though a new row had been entered into the DataGridView. If a row had been retrieved, it would be in dtDep and the database table updated by the last statement.
this.Validate();
bsBelkDep.EndEdit();
DataTable dtDep = dsBelk.Tables["belk_elig_dep"];
int n = belk_elig_depTableAdapter.Update(this.dsBelk.belk_elig_dep);
It was a problem with the DataGridView, but I don't know what. I started deleting and re-creating the various object, and after the I recreated the DataGridView, it worked OK. Which was a pain because I have to do significant reformatting, but at least it works.
This is a very old question and I have no way of knowing if it was the OP's original problem, but I had the exact same scenario and this is how I resolved it.
For background: I have a WinForms application built using datasets and an Access database. I migrated that to use Sqlite and anything but datasets. To avoid destroying the application completely, first I copied the strongly typed data tables out, tweaked them to account for changes in the schema and then used PetaPoco to perform the data operations. That worked fine for a single test conversion.
The trouble arose when I wanted to move on and convert all data tables - I wasn't happy manually writing the logic for converting to and from typed data rows and POCOs, so I fell back to writing old school T4 templates to generate typed DataTable, DataRow classes and the necessary remapping code.
Worked a treat - for editing or removing data. But new rows disappeared on "creation", the binding navigator count didn't increment, and of course, when saving, I didn't detect any rows with the RowState of DataRowState.Added. The grid at start up was subtly different - a blank value in all columns instead of a negative number in the ID column. In hindsight, that should have been a big clue.
On reverting the behaviour back to the manually extracted typed class the grid started working again so it was clearly an error in the new code.
End of background; tldr;
The cause of the issue, in my case, was that the my Id column didn't have the AutoIncrement property set. As soon as I configured that to be true (along with setting AutoIncrementSeed and AutoIncrementStep to -1, although neither are required) new rows started being correctly added to the table.

Database not updating when using DataSet

I am doing a small project to learn how to use DataSet but i have a small problem. Consider following code:
foreach (DatabaseDataSet.ApplicationRow rowApplication in database.Application)
{
if (rowApplication.AID.ToString() == lblIDApplication.Text)
{
rowApplication.Date= tbApplicationDatum.Text;
rowApplication.Status = tbApplicationStatus.Text;
applicationAdapter.Update(rowApplication);
break;
}
}
I don't know why but the database doesn't get updated. The DataRow is being updated as when I call the data again I see the new value. But when I re-run my application it's back to it's old value again. Any help?
EDIT: I'm working with strongly typed DataSet
You need to call the Update method of your adapter to propogate the changes
AcceptChanges only updates the changes in memory for the row and does not migrate those to the database
MSDN
AcceptChanges and RejectChanges only apply to DataRow related changes
(that is, Add, Remove, Delete, and Modify). They are not applicable to
schema or structural changes.
Calling AcceptChanges will not replicate these changes back to the
data source if the DataSet was filled using a DataAdapter. In that
situation, call Update instead
See Updating Data Sources with DataAdapters for more information
Its important to remember that the DataSet is a 'local copy' of the data not a 'live link' to the DB. If your DataSet is populated by a IDataAdaptor (say a TableAdaptor) for example you need to call the DataAdaptors Update method passing in the Updated dataset to sync the results back to the underlying DB.
Also I would suspect you DONT want to be doing 'new ApplicationTableAdapter()' because typically you would want to update with the TableAdaptor you populated with, at the least you would need to ensure you had the correct connection, query etc set up.
SOLUTION: It happens that nothing was wrong with the code. I had two ConnectionString defined in App.config. I forgot to remove the first one after I removed a previous database that had errors in it. Upon removing the first ConnectionString, everything worked.

Linq DataContext SubmitChanges InvalidOperationException from ZombieCheck

I am getting an InvalidOperationException when trying to add a row using LinqToSql. We cannot duplicate it in house, and it happens about 0.06% for only one of our customers, always on a relatively simple change to the database. (Single row insert, or single field update)
Message:
This SqlTransaction has completed; it is no longer usable.
Stack Trace:
at System.Data.SqlClient.SqlTransaction.ZombieCheck()
at System.Data.SqlClient.SqlTransaction.Rollback()
at System.Data.Linq.DataContext.SubmitChanges(ConflictMode failureMode)
Here is a sample piece of code (the database autogenerates the primary key)
TableName row = new TableName();
row.Description = "something";
row.Action = "action";
Context.TableName.InsertOnSubmit(row);
Context.SubmitChanges();
We use SQL Server 2008 R2. The inserts and updates do go through on the server. But we still get the exception. There is nothing that should ever prevent these updates and inserts from taking place. No dependencies or other stuff.
How do we stop these exceptions / zombie checks / rollbacks from happening, or what is causing them in the first place?
EDIT:
After further inspection, the database update that being done by the SubmitChanges() is actually occurring. This exception is getting called after the transaction has successfully completed, and the database row is updated to the new value.
One thing to be aware of is that LinqToSql (and EntityFramework) will by default assign null to DateTime fields in your data objects, so if your table has a datetime field it will throw an exception on insert if the datacontext tries to insert that null value.
You can get around this error by either using the datetime2 type in MSSQL (which will allow the "null" value of a DateTime object - 01/01/0001) or manually assigning a valid date to the data object's DateTime field(s) prior to insert/update.
Without a more detailed stack trace, this is the only obvious problem that comes to mind. HTH.
EDIT:
Looks like this isn't entirely uncommon: http://connect.microsoft.com/VisualStudio/feedback/details/588676/system-data-linq-datacontext-submitchanges-causes-invalidoperationexception-during-rollback#details
The root problem seems to be that the internal ADO logic that LinqToSql uses isn't really configured properly for handling transactional rollbacks. From what I can tell, the only real solution is to provide a transaction object to LinqToSql and manage rollbacks yourself, which doesn't really seem all that appealing.

Categories