Sync framework get conflicting row data - c#

I am using Sync Framework to sync two Sql Server databases.
When some conflict is detected I catch the event and do some logic to resolve it, but I cant get the row data of the conflict.
So my question is, how can I get the specific conflicting data in the row that are about to synchronize?
The following code illustrates what I am doing:
if (e.Conflict.Type == DbConflictType.LocalInsertRemoteInsert)
{
DataTable conflictingRemoteChange = e.Conflict.RemoteChange;
}

When the ApplyChangeFailed event is raised by SqlSyncProvider, you use the DbApplyChangeFailedEventArgs object which provides information about the error or conflict that caused the failure. The e.conflict property contains moreover:
RemoteChange: Gets the DataTable object that contains the
conflicting rows from the remote database.
LocalChange: Gets the DataTable object that contains the conflicting rows from the local database.
These tables contain each one at most one row that represents the row in conflict.
Datarow drRemote = e.Conflict.RemoteChange.Rows[0];
Datarow drLocal = e.Conflict.LocalChange.Rows[0];
To get the specific value in conflict in the row, I'm afraid there is no other way that manually comparing of each value from the DataRows.
EDIT : #JuneT says that better than I ;-)
You can only get the rows in conflict. it will not tell you which
columns are in conflict. Sync Framework change tracking is at row
level. it simply logs that a row has changed and doesn't care what
changed.

Related

C# System.Data.SQLite : Concurrency violation: the UpdateCommand affected 0 of the expected 1 records

I have a .net winforms application which is using SQLite DB and I am using System.Data.SQLite dll.
This is how I load a Datatable:
DataTable table = new DataTable();
SQLiteDataAdapter m_readingsDataTableDataAdapter;
SQLiteCommandBuilder m_readingsDataTableCommandBuilder;
m_readingsDataTableCommandBuilder = null;
m_readingsDataTableDataAdapter = null;
// Create fresh objects
m_readingsDataTableDataAdapter = new SQLiteDataAdapter(sql, Database.getInstance().connectionObj);
m_readingsDataTableCommandBuilder = new SQLiteCommandBuilder(m_readingsDataTableDataAdapter);
m_readingsDataTableDataAdapter.Fill(table);
return table;
This data table has one primary key and no other constraints.
I set it as a data source for DataGridView and after all the edits, I update the DataTable like this:
m_readingsDataTableDataAdapter.Update(table);
Occasionally, the updates fires an error and I don't know when, it stops throwing errors - probably after system restart ( not sure ). And then, the updates go fine until another situation where this update throws an error again. When the error occurs, it happens for all updates from then on even after application restart.
Error:
An unhandled exception of type 'System.Data.DBConcurrencyException' occurred in System.Data.dll
Additional information: Concurrency violation: the UpdateCommand affected 0 of the expected 1 records.
I would appreciate any help or questions as this is quite a critical section of my project.
Thanks.
Update:
Based on suggestions, to ensure no other part of the program is editing the row, I loaded the DataTable, updated the row immediately and updated it as per the following code. And I still got the error. There is no other program running on my machine so the row is not updated by any other program except my application:
DataTable table = new DataTable();
SQLiteDataAdapter m_readingsDataTableDataAdapter;
SQLiteCommandBuilder m_readingsDataTableCommandBuilder;
m_readingsDataTableCommandBuilder = null;
m_readingsDataTableDataAdapter = null;
// Create fresh objects
m_readingsDataTableDataAdapter = new SQLiteDataAdapter(sql, Database.getInstance().connectionObj);
m_readingsDataTableCommandBuilder = new SQLiteCommandBuilder(m_readingsDataTableDataAdapter);
m_readingsDataTableDataAdapter.Fill(table);
// Update immediately
table.Rows[0]["RS485_ADDRESS"] = "400";
m_readingsDataTableDataAdapter.Update(table); // - Still throws error
return table;
I had a similar issue that drove me nearly mad. I even debugged into System.Data.SQLite . My issue was caused by the dynamic typing of SQLite:
Dynamic typing means that the column type is just a hint for the type of the stored value but not an enforcement. Here is what happened in my application: An integer column contained an empty string. Fetching the data triggered an implicit conversion to the column type so the empty string was converted to zero. The DbDataAdapter used this zero value in the WHERE clause of an UPDATE or DELETE statement which failed miserably because the conversion of zero to a string is not an empty string. I changed the affected integer columns to contain NULL and everything is fine now.
The sad fact about that: It is not a bug in SQLite or ADO.NET . DbAdapter and DbCommandBuilder expect strong typing and SQLite doesn't provide that by design.
I've seen this problem, it seemed to appear in some cases and not others. I have resolved it a different way. Since this is primarily a data issue, it involves changing the update and delete statements. There may be cases where your code might need to be validated, I'm not referring to those cases, this is for when all else seem to be in order and "Concurrency Violation" persists. And a good starting place before you spend hours debugging is to address how the updates are handled.
Open the Data Set Designer.
Click on the Table Adapter.
In the properties, expand the UpdateCommand
Edit the SQL so the Where-Clause refers only to the primary-key.
Example: Suppose the column EmployeeID is the primary-key in our table
Change to:
update ..... WHERE EmployeeID=#EmployeeID
By default it's usually constructed as:
update ... where ID=#ID AND col1=#col1 AND col2=#col2 AND col3=#col3
The where-clause is formed with all the columns to handle multi-user updates, to enforce an implicit concurrency. So if one user has a copy of the record, and another user updates that version by the time the first user updates it, first user's version is not current. Otherwise, primary-keys should be used to refer to a specific record.
But, this is an update/delete policy decision that the development and database teams need to coordinate. For a single developer/dba in-one, have a little meeting with yourself. Don't take the Microsoft where-clause without your review and understanding and explicit implementation. Need to specifically address what happens when 2 users have a copy of the same record, both from the application and database levels. Database "Transaction Isolation" Levels in SQL Server is a way to address it. ADO, ADO.NET, OLE DB, ODBC all have isolation level settings. More on SQL Server Transactions and isolation levels
In some cases multiple users are not involved, or the application is updating internally, concurrency is fully controlled, where-clause can be formed with the primary-key.
In my case, the update is done without a user involvement, so it makes sense to use the primary key. If you understand the reasoning and the cause of the behavior, it's a matter of adjusting to fit your environment.

Datatable won't update my DB

usersTableAdapter usersTA = new usersTableAdapter();
var usersTable = new users.usersDataTable();
usersTA.Fill(usersTable);
VK_BDayParser.users.usersRow row = usersTable.FindByid(currentItem.id);
row.BeginEdit();
row.last_name = "********";
row.EndEdit();
row.AcceptChanges();
usersTable.AcceptChanges();
int result = usersTA.Update(usersTable);
SQL server 2012.
I used generated classes from VS 2013 to database
I try to update row in my DB, in 'usersRow row' this changes works, but in DB has no changes. What am I doing wrong? result is always 0.
This is a common misunderstanding on what the AcceptChanges method does.
I think the problem arises from the initial comment in the documentation of the AcceptChanges method .
Commits all the changes made to this table since the last time
AcceptChanges was called.
Many people think that this means 'commits to the database table', instead it means 'commits to the in-memory instance of the datatable object'.
Then, the following comment on the docs, hints to what is really happening there.
When AcceptChanges is called, any DataRow object still in edit mode
successfully ends its edits. The DataRowState also changes: all Added
and Modified rows become Unchanged, and Deleted rows are removed.
So, there is this DataRow.RowState property that express what is the current state of the in-memory rows belonging to the in-memory instance of the DataTable. And it is this state that helps the DataAdapter.Update method in discovering what to do with the rows.
In other words the Update method decides to update the database table only for the rows that are not RowState==DataRowState.Unchanged. But calling AcceptChanges 'commits' these rows and their state becomes Unchanged. No update then.

Insert a DataRow inside a table

I have a function that returns a typed DataRow object and now I would insert this DataRow in a given table. As far as I know I could do this using TableAdapter in two ways: TableAdapter.Update and TableAdapter.Insert.
Both ways seems pretty simple to use but, first way could even delete row from the table (essentially my DataSet will be "serialized" on my DB table) and since this table is really the application's critical part I would avoid any chance of data deletion, so I would prefear Insert method.
The problem seems to be that Insert method doesn't accept a DataRow object as it's parameter (unlike Update), so I need to manually insert each parameters. Alas I have at least 80 parameters so this is a really really headache operation. What can I do now?
The update method does allow you to insert,update or delete changes in the tableAdapter. MSDN: 'Use the TableAdapter.Update method when your application uses datasets to store data. The Update method sends all changes (updates, inserts, and deletes) to the database'. Although Insert is ment for usage if you want more control for you data inserts but does not allow you to pass a datarow instead Parameters must be used. See complete reference on MSDN: http://msdn.microsoft.com/en-us/library/ms233812(v=vs.110).aspx
Code example of Update method usage.
// Create a new row.
NorthwindDataSet.RegionRow newRegionRow;
newRegionRow = northwindDataSet.Region.NewRegionRow();
newRegionRow.RegionID = 5;
newRegionRow.RegionDescription = "NorthWestern";
// Add the row to the Region table
this.northwindDataSet.Region.Rows.Add(newRegionRow);
// Save the new row to the database
this.regionTableAdapter.Update(this.northwindDataSet.Region);
Code example Insert:
NorthwindDataSetTableAdapters.RegionTableAdapter regionTableAdapter =
new NorthwindDataSetTableAdapters.RegionTableAdapter();
regionTableAdapter.Insert(5, "NorthWestern");

Can I access entire DataTable if all I have is a single DataRow?

DataRow contains a Table property, which seems to return the entire Table for which this row belongs.
I'd like to know if I can use that table safely, or if there are gotcha's.
In http://msdn.microsoft.com/en-us/library/system.data.datarow.table.aspx documentation, it says "A DataRow does not necessarily belong to any table's collection of rows. This behavior occurs when the DataRow has been created but not added to the DataRowCollection.", but I know for a fact my row belongs to a table.
In terms of pointers, if each Row from DataTable points to original DataTable, than I'm good to go. Is that all 'Table' property does?
Just to explain why I'm trying to get entire Table based on a single DataRow:
I'm using linq to join two (sometimes more) tables. I'd like to have a generic routine which takes the output of linq (var), and generate a single DataTable with all results.
I had opened another question at stackoverflow (Join in LINQ that avoids explicitly naming properties in "new {}"?), but so far there doesn't seem to be a generic solution, so I'm trying to write one.
if you know the row is part of table than yes you can access it without any problem. if the possibility exists where the row may not be associated to a table than check if the property is null.
if(row.Table == null)
{
}
else
{
}
As long as it's not null, you can use it freely.

Remove record from a datatable when record disappears from a mirrored datatable

I have two mirrored datatables (same structure with two primary keys) :
DataTable_A ---> bound to a datagridView
DataTable_B ---> filled from a database
Since DataTable_B is filled by a query into database every 2 seconds, I need to mirror the DataTable_A like DataTable_B avoiding filling directly DataTable_A. When a record disappears from DataTable_B i need to delete the record also from DataTable_A. What is the best way to do this ?
Right now I am doing a "for cycle" on each row of DataTable_B and if the row doesn't exist on DataTable_A, I delete it.
Is there a better way to do it ?
The best way may be not to have a TableA at all but use a DataView on TableB. That would solve all problems at once. Can you elaborate on why you need the copy?
But otherwise you would want to handle the RowChanged and TableNewRow RowDeleted event of TableB
A more general idea, after seeing your comments: If it is possible to add a Timestamp column to the table in the database you can run a much more efficient query. And the DataTable.Merge method would do the rest.

Categories

Resources