I have the following code below with different calls to stored procedures.
My problem is that I want this to be in a transaction, so if one of the DB methods fails then it will automatically rollback the pending changes.
I have read some articles on Stackoverflow/Microsoft pages which states that the SubmitChanges will wrap these in transactions for me.
The problem is that the spClearTablesForReplication method clears the tables and later on the code fails on purpose (for testing) and then all my tables are empty (is not rolled back)
XalSqlDataContext db = new XalSqlDataContext();
db.spClearTablesForReplication();
db.spUpdateStockItemGroup(ConvertToXElement(typeof(List<StockItemGroup>), stockItemGroups));
db.spUpdateStockItemSubGroup(ConvertToXElement(typeof(List<StockItemSubGroup>), stockItemSubGroups));
db.SubmitChanges();
Any clues/solutions for this.
Wrap your code in a TransactionScope using block and then that will rollback unless it reaches the point where you call transactionScope.Complete().
Example:
using (TransactionScope transactionScope = new TransactionScope())
{
//code here
transactionScope.Complete();
}
Related
We are using our select statement inside transaction scope because of concurrency concerns.
The question is, if I put my transaction in using statement, do I still have to call Commit() method explicitly to be sure that the transaction is closed or Dispose() method will do the job?
Here's example code:
await using (var transaction = await Context.BeginTransactionAsync())
{
callbackUrl = await this.SomeRepository.GetResultAsync(request);
await transaction.CommitAsync();
}
using is only calling a Dispose() method at the end. It will not Commit the transaction automatically.
Disposing also rolls back all the changes made within transaction. If you dispose before commit or just dispose it will roll back all the changes and close the transaction.
If there are no changes you don't have to commit the transaction to close it. You can just wait for the using to do it's job and dispose the transaction which will close it anyway.
Generally if you don't expect anything to be commited just don't use commit.
The official docs would be your best friend in such situations.
Once you choose handle transactions manually, you should call commit or rollback in your program flow.
EFCore's transaction already have an atomic behavior for the SaveChanges method: it will commit it's implicit transaction changes if everything succeed and rollback on any error occurrence.
On a first sight of your code, there is no need to call commit (or even use transaction) once you are not making any changes to database, its just a query. I bet you are using this like a to change the context's query isolation level, is that right?
Maybe you should elaborate a little bit more of your context and the 'concurrency concerns' to get a more effective help.
I would like to be 100% that when I use such pattern
using (var scope = new TransactionScope())
{
db1.Foo.InsertOnSubmit(new Foo()); // just an example
db1.SubmitChanges();
scope.Commit();
}
the transaction scope will only refer to db1 not db2 (db1 and db2 are DataBaseContext objects). So how to limit scope to db1 only?
Thank you in advance.
The strength of TransactionScope is that it automatically catches everything within it, down the call stack (unless another TransactionScope with RequiresNew or Supress). If this is not what you want, you should use another mechanism for the transaction handling.
One way is to open SqlConnection and call BeginTransaction to get a transaction, then use that DB connection when creating your data context. (Maybe you'll have to set the Transaction propery on the data context, I'm not sure).
In the example you have given above, the use of a TransactionScope is totally redundant. There is only one function call that actually modifies the database: SubmitChanges and it always creates its own transaction if there isn't already one existing. The reason is that when you're doing several operations SubmitChanges should either succeed in them all or fail all. If you're only after transactional integrity for a single SubmitChanges call for a single data context, then you can just drop the TransactionScope.
As far as I know, what you are after is exactly what you have written, the compiler will ensure that transaction is completed when you commit.
If you use:
using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew))
{
db1.Foo.InsertOnSubmit(new Foo()); // just an example
db1.SubmitChanges();
scope.Commit();
}
You can be sure a new transcation is created for this scope, so everyting you do inside the scope has its own transaction, and will never use any existing ambient transactions.
I'm having a major performance issue with LINQ2SQL and transactions. My code does the following using IDE generated LINQ2SQL code:
Run a stored proc checking for an existing record
Create the record if it doesn't exist
Run a stored proc that wraps its own code in a transaction
When I run the code with no transaction scope, I get 20 iterations per second. As soon as I wrap the code in a transaction scope, it drops to 3-4 iterations per second. I don't understand why the addition of a transaction at the top level reduces the performance by so much. Please help?
Psuedo stored proc with transaction:
begin transaction
update some_table_1;
insert into some_table_2;
commit transaction;
select some, return, values
Pseudo LINQ code without transaction:
var db = new SomeDbContext();
var exists = db.RecordExists(some arguments);
if (!exists) {
var record = new SomeRecord
{
// Assign property values
};
db.RecordsTable.InsertOnSubmit(record);
db.SubmitChanges();
var result = db.SomeStoredProcWithTransactions();
}
Pseudo LINQ code with transaction:
var db = new SomeDbContext();
var exists = db.RecordExists(some arguments);
if (!exists) {
using (var ts = new TransactionScope())
{
var record = new SomeRecord
{
// Assign property values
};
db.RecordsTable.InsertOnSubmit(record);
db.SubmitChanges();
var result = db.SomeStoredProcWithTransactions();
ts.Complete();
}
}
I know the transaction isn't being escalated to the DTC because I've disabled the DTC. SQL Profiler shows that several of the queries take much longer with the transactionscope enabled, but I'm not sure why. The queries involved are very short lived and I've got indexes that I have verified are being used. I'm unable to determine why the addition of a parent transaction causes so much degredation in performance.
Any ideas?
EDIT:
I've traced the problem to the following query within the final stored procedure:
if exists
(
select * from entries where
ProfileID = #ProfileID and
Created >= #PeriodStart and
Created < #PeriodEnd
) set #Exists = 1;
If I had with(nolock) as shown below, the problem disappears.
if exists
(
select * from entries with(nolock) where
ProfileID = #ProfileID and
Created >= #PeriodStart and
Created < #PeriodEnd
) set #Exists = 1;
However, I'm concerned that doing so may cause problems down the road. Any advice?
One big thing that changes as soon as you get a transaction - the isolation level. Is your database under heavy contention? If so: by default a TransactionScope is at the highest "serializable" isolation level, which involves read locks, key-range locks, etc. If it can't acquire those locks immediately it will slow down while it it blocked. You could investigate by reducing the isolation level of the transaction (via the constructor). For example (but pick your own isolation-level):
using(var tran = new TransactionScope(TransactionScopeOption.Required,
new TransactionOptions { IsolationLevel = IsolationLevel.Snapshot })) {
// code
tran.Complete();
}
However, picking an isolation level is... tricky; serializable is the safest (hence the default). You can also use granular hints (but not via LINQ-to-SQL) such as NOLOCK and UPDLOCK to help control locking of specific tables.
You could also investigate whether the slowdown is due to trying to talk to DTC. Enable DTC and see if it speeds up. The LTM is good, but I've seen composite operations to a single database escalate to DTC before...
Although you are using a single datacontext, your code sample is likely to use more than one connection and that will escalate your transaction to a distributed transaction.
Try initializing your datacontext with an explicit db connection, or call db.Connection.Open() right after creating the datacontext. That removes the overhead of distributed transactions...
Does the Stored Procedure you call participate in the ambient (parent) transaction? - that is the question.
It's likely that the Stored Procedure participates in the ambient transaction, which is causing the degredation. There's an MSDN article here discussing how they interrelate.
From the article:
"When a TransactionScope object joins an existing ambient transaction, disposing of the scope object may not end the transaction, unless the scope aborts the transaction. If the ambient transaction was created by a root scope, only when the root scope is disposed of, does Commit get called on the transaction. If the transaction was created manually, the transaction ends when it is either aborted, or committed by its creator."
There's also a serious looking document on nested transactions which looks like it is directly applicable localted on MSDN here.
Note:
"If TransProc is called when a transaction is active, the nested transaction in TransProc is largely ignored, and its INSERT statements are committed or rolled back based on the final action taken for the outer transaction."
I think that explains the difference in performance - it's essentially the cost of maintaining the parent transaction. Kristofer's suggestion may help to reduce the overhead.
I've got an IDbTransaction in a using statement but I'm unsure if it will be rolled back if an exception is thrown in a using statement. I know that a using statement will enforce the calling of Dispose()...but does anyone know if the same is true for Rollback()?
Update: Also, do I need to call Commit() explicitly as I have below or will that also be taken care of by the using statement?
My code looks sort of like this:
using Microsoft.Practices.EnterpriseLibrary.Data;
...
using(IDbConnection connection = DatabaseInstance.CreateConnection())
{
connection.Open();
using(IDbTransaction transaction = connection.BeginTransaction())
{
//Attempt to do stuff in the database
//potentially throw an exception
transaction.Commit();
}
}
Dispose method for transaction class performs a rollback while Oracle's class doesn't. So from transaction's perspective it's implementation dependent.
The using statement for the connection object on the other hand would either close the connection to the database or return the connection to the pool after resetting it. In either case, the outstanding transactions should be rolled back. That's why an exception never leaves an active transaction lying around.
Also, yes, you should call Commit() explicitly.
You have to call commit. The using statement will not commit anything for you.
I believe that if there's an exception such that Commit() was never called, then the transaction will automatically rollback.
I'm writing an integration test where I will be inserting a number of objects into a database and then checking to make sure whether my method retrieves those objects.
My connection to the database is through NHibernate...and my usual method of creating such a test would be to do the following:
NHibernateSession.BeginTransaction();
//use nhibernate to insert objects into database
//retrieve objects via my method
//verify actual objects returned are the same as those inserted
NHibernateSession.RollbackTransaction();
However, I've recently found out about TransactionScope which apparently can be used for this very purpose...
Some example code I've found is as follows:
public static int AddDepartmentWithEmployees(Department dept)
{
int res = 0;
DepartmentAdapter deptAdapter = new DepartmentAdapter();
EmployeeAdapter empAdapter = new EmployeeAdapter();
using (TransactionScope txScope = new TransactionScope())
{
res += deptAdapter.Insert(dept.DepartmentName);
//Custom method made to return Department ID
//after inserting the department "Identity Column"
dept.DepartmentID = deptAdapter.GetInsertReturnValue();
foreach(Employee emp in dept.Employees)
{
emp.EmployeeDeptID = dept.DepartmentID;
res += empAdapter.Insert(emp.EmployeeName, emp.EmployeeDeptID);
}
txScope.Complete();
}
return res;
}
I believe that if I don't include the line txScope.Complete() that the data inserted will be rolled back. But unfortunately I don't understand how that is possible... how does the txScope object keep a track of the deptAdapter and empAdapter objects and their transactions on the database.
I feel like I'm missing a bit of information here...am I really able to replace my BeginTransaction() and RollbackTransaction() calls by surrounding my code using TransactionScope?
If not, how then does TransactionScope work to roll back transactions?
Essentially TransactionScope doesn't track your Adapter's, what it does is it tracks database connections. When you open a DB connection the connections will looks if there is an ambient transaction (Transaction Scope) and if so enlist with it. Caution if there are more the one connection to the same SQL server this will escalate to a Distribtued Transaction.
What happens since you're using a using block you are ensuring dispose will be called even if an exception occurs. So if dispose is called before txScope.Complete() the TransactionScope will tell the connections to rollback their transactions (or the DTC).
The TransactionScope class works with the Transaction class, which is thread-specific.
When the TransactionScope is created, it checks to see if there is a Transaction for the thread; if one exists then it uses that, otherwise, it creates a new one and pushes it onto the stack.
If it uses an existing one, then it just increments a counter for releases (since you have to call Dispose on it). On the last release, if the Transaction was not comitted, it rolls back all the work.
As for why classes seem to magically know about transactions, that is left as an implementation detail for those classes that wish to work with this model.
When you create your deptAdapter and emptAdapter instances, they check to see if there is a current transaction on the thread (the static Current property on the Transaction class). If there is, then it registers itself with the Transaction, to take part in the commit/rollback sequence (which Transaction controls, and might propogate to varying transaction coordinators, such as kernel, distributed, etc.).