MAJ EF Core 3.1 using Transaction, MultipleContext and Trigger - triggers

I want to update multiple databases but i have a error : "An exception has been raised that is likely due to a transient failure. Consider enabling transient error resiliency by adding 'EnableRetryOnFailure()' to the 'UseMySql' call."
During the Context2.SaveChanges() i have the inner exception is: "Lock wait timeout exceeded; try restarting transaction", because the table B has a trigger "after update" and he read into table A in the context1.
I use a TransactionScope and two different context:
using (TransactionScope transactionScope = new
TransactionScope(TransactionScopeOption.Required, _timeSpan))
{
....
//update the first database, table A
Context1.SaveChanges();
....
//update the second database, table B
Context2.SaveChanges();
.....
// Commit transaction if all commands succeed, transaction will auto-rollback when disposed if either commands fails.
transactionScope.Complete();
return result;
}
The trigger update a table in the Context2 and he read in the Context1 in the table A.
Somebody have you a idea for a solution ?

Related

Entity Framework Optimistic Concurrency Exception with Multiple records updated

When updating multiple records in the database via the entity framework database context, one record is throwing an optimistic concurrency exception and that record is not updated in the database. But I am finding the other records updated are persisted to the database. Should the optimistic concurrency exception not roll back all changes made prior to the optimistic concurrency exception?
I bet exception is thrown by EF library whenever update was expected, but SQL Server response indicates none was made.
Exception thrown by DbContext when it was expected that SaveChanges for an entity would result in a database update but in fact no rows in the database were affected. This usually indicates that the database has been concurrently updated such that a concurrency token that was expected to match did not actually match. Note that state entries referenced by this exception are not serialized due to security and accesses to the state entries after serialization will return null.
Also Entity Framework translates into single SQL UPDATE statement per object e.g.
exec sp_executesql N'SET NOCOUNT ON;
UPDATE [Students] SET [Name] = #p0
WHERE [StudentId] = #p1;
SELECT ##ROWCOUNT;
UPDATE [Students] SET [Name] = #p2
WHERE [StudentId] = #p3;
SELECT ##ROWCOUNT;
UPDATE [Students] SET [Name] = #p4
WHERE [StudentId] = #p5;
SELECT ##ROWCOUNT;
',N'#p1 int,#p0 nvarchar(4000),#p3 int,#p2 nvarchar(4000),#p5 int,#p4
nvarchar(4000)',
#p1=1,#p0=N'Bill',#p3=2,#p2=N'Steve',#p5=3,#p4=N'James'
go
Which means everything is okay by SQL Server and transaction is commited by Entity Framework. Only EF throws exception to let you know something went wrong. In your case one of those statements updates nothing and number of updated rows was/is 0.
If you need to rollback all changes during such an update you have to encapsulate it with another transaction. Commit and rollback as needed.
using (var dbContextTransaction = context.Database.BeginTransaction())
{
try
{
// Do your stuff
context.SaveChanges();
dbContextTransaction.Commit();
}
catch (DbUpdateConcurrencyException)
{
// Expected
dbContextTransaction.Rollback();
}
catch (Exception)
{
// Unexpected
dbContextTransaction.Rollback();
}
}
Relevant reading:
More about DbUpdateConcurrencyException Class
More about Update Data in Disconnected Scenario in Entity Framework Core (Update Multiple Entities)
More about Entity Framework Working with Transactions

How to handle TransactionScope on multi-instance applications?

I'm using Entity Framework 5.0. I need to restrict access to a row while I'm reading and Updating it.
My application run on more that 10 machines and when I use TransactionScope ,some time some other application on other machines (randomly) dump and can not update or read data from that table.
I think TransactionScope restricted access to my table while its reading or updating and other update or reading request will dump.
How can I handle other requests from other applications to update or read data from that table when one application did not done TransactionScope action?
How can I handle it?
using (var myDB = new MyDBEntities())
{
using (TransactionScope scope = new TransactionScope())
{
// read and update myDB object with some code in here
// ...
myDB.SaveChanges();
scope.Complete();
}
}
When using transaction scopes you can hold another transaction to select/update the same row.
Also, you can hide uncommitted data from another transaction using special table hint called as READPAST.
Example:
session1
BEGIN TRANSACTION
update users with (SERIALIZABLE) set name='test' where Id = 1
-- COMMIT --not committed yet
session2
select * from users where Id = 1
--waits session1 to finish its job
--rows returns after commit
session3
select * from users with (READPAST) where Id = 1 --returns 0 row
While you're not commit the transaction, other sessions could not read or update the data. When you commit the transaction on session1, session2 will be able to read the row.
http://omerc.blogspot.com.tr/2010/04/transaction-and-locks-in-ms-sql-2008.html
https://technet.microsoft.com/en-us/library/jj856598(v=sql.110).aspx
https://www.codeproject.com/Articles/690136/All-About-TransactionScope
Be aware that data is still open to lost updates. To prevent it you may consider to use optimistic/pessimistic locking.
https://logicalread.com/sql-server-concurrency-lost-updates-w01/#.WskuNNNuZ-U

Zend: Rollback a commit?

Let's say I start a transaction and then finish it with a commit:
$db->beginTransaction();
// sql insert
// another sql insert
// a sql update
// another sql update
$db->commit();
Is there a function I can run after commit() to revert all the changes made by between beginTransaction and commit() ?
After commit you cannot revert changes for reverting you should call rollback instead.
Rollback action done mainly if any one entity in a transaction fails.Therefore to catch a failure you should use try{}catch{}
$db->beginTransaction();
try{
$db->commit();//writes all data to database and reach to new state
}catch(Exception $e)
{
$db->rollback();//roll back all changes made to database
echo $e->getTraceAsString();
}
This will revert changes from the current transaction:
$db->rollBack();
However, you cannot rollback after you have committed.
If you are trying to prevent inserting on errors etc. I would try this:
try
{
$db->beginTransaction();
// sql insert
// another sql insert
// a sql update
// another sql update
$db->commit();
}
catch( Zend_Exception $e)
{
$db->rollBack();
}
This will commit if there are no errors, and rollback if there are.

EJB3.0 Updating a field in a table with the same value

I have a database table 'MyTable' that has a trigger upon update of field 'Status' in it.
Below is a dummy-code of what i'm trying to do:
MyTable table = new Mytable();
table.setTableId(1);
table.setStatus ("NEW");
em.persist (table); //At this point the trigger did not kick in since this inserted a new record
...
MyTable table2 = em.find(MyTable.class, 1);
table2.setStatus ("NEW");
em.merge(table2)//Even though im updating the record with the same status with the same value, i still want the trigger to kick. However the trigger is not being activated.
...
MyTable table3 = em.find(MyTable.class, 1);
table3.setStatus ("OLD");
em.merge(table3)//The trigger is being activated here since the status is different the status value when it was inserted the first time.
Long story short, how can i make the changes done to 'transfer2' to trigger an update even though the status is the same?
-Thanks
Use em.flush(); to synchronize the persistence context to the underlying database. Your pending queries shall be send to database, but you still can have a complete rollback.
JPA does not update object that have no changes.
You could try changing to something else (flush), then changing it back.
You could also use a JPQL update query to update it.
Depending on your JPA provider you could probably force it to update fields that have not changed, but this would lead to very bad performance.
Could you please try updating the enity and commit the transaction, without using merge.
Like
em.getTransaction().begin();
MyTable table2 = em.find(MyTable.class, 1);
table2.setStatus ("NEW");
//em.merge(table2)//Even though im updating the record with the same status with the
// same value, i still want the trigger to kick. However the trigger is not being activated.
em.getTransaction().commit();

Npgsql with PostgreSQL: Can't see uncommitted changes with UNCOMMITTED READ

I'm using Npsql with PostgreSQL. I want to see uncommitted changes of one transaction in a different one.
This is how I create my connection and transaction:
// create connection
m_Connection = new NpgsqlConnection(connectionString);
m_Connection.Open();
//create transaction
m_Transaction = m_Connection.BeginTransaction(IsolationLevel.ReadUncommitted);
In one thread I insert a row like so:
NpgsqlCommand command = CreateCommand("INSERT INTO TABLEA ....", parameters, commandTimeout)
command.ExecuteNonQuery();
and process something else without committing or rolling back the transaction.
In a different thread I read a row like so:
NpgsqlCommand command = CreateCommand("SELECT COUNT(*) FROM TABLEA", parameters, commandTimeout);
command.ExecuteScalar();
but somehow I don't see the results of the first INSERT. I don't see the results of the insert in pgAdmin either (even after running SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED).
What am I doing wrong? Any help will be appreciated.
PostgreSQL does not support uncommitted reads.
You will never be able to see changes from other transactions that are not committed.