Sql client transactions from code vs database-controlled transactions - c#

I've always done transactions from within stored procedures but now I need to wrap a bunch of "dynamic" statements executed from code against sp_executesql in a transaction.
Specifically I need the READ UNCOMMITED isolation level for these in some cases (I know what that does, and yes, that's what I need). This is SQL2008.
My question is this: If I use the BeginTransaction() method of my SqlConnection instance with the isolation level set to IsolationLevel.ReadUncommitted will that have the same effect as if I executed a stored proc that has the READ UNCOMMITED statement?

Yes, it will.
The SqlConnection uses the SQL native client, and a call to BeginTransaction causes exactly this to be sent to the server:
SET TRANSACTION ISOLATION LEVEL <WHATEVER>; BEGIN TRANSACTION;

Related

SET TRANSACTION ISOLATION LEVEL works only with transactions?

In the official example here we have the SET TRANSACTION ISOLATION LEVEL being used in conjunction with an explicitly defined transaction.
My question is, if I execute a query from a SqlCommand, like:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT * from MyTable
would I benefit from the new isolation level I set?
Or do I need to explicitly define a transaction like this?
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
BEGIN TRANSACTION;
SELECT * from MyTable
COMMIT TRANSACTION;
UPDATE:
As per Randy Levy's answer, I will update my query as follows:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT * from MyTable;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
This is to overcome possible isolation level leaks when using pooling.
Yes, you would benefit from the transaction isolation level that you set even if not within an explicit BEGIN TRANSACTION. When you set the transaction isolation level it is set on a connection level.
From SET TRANSACTION ISOLATION LEVEL (Transact-SQL):
Only one of the isolation level options can be set at a time, and it
remains set for that connection until it is explicitly changed.
One "gotcha" (issue) that can occur is that the isolation level can leak between different connections when using pooling. If you are explicitly setting an isolation level in one (or some) particular piece(s) of code (but using the default most other places) and also using connection pooling. This can cause strange issues if code expects the default isolation level "A" but obtains a connection that had the isolation level explicitly set to "B".
It seems this issue is now fixed in later versions of SQL Server: SQL Server: Isolation level leaks across pooled connections
The first one
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT * from MyTable
will work. The transaction level you set applies to each subsequent transaction, and your SELECT statement is its own implicit transaction.
You would only need to explicitly start a transaction if you needed to ensure some degree of consistency throughout multiple reads. For example if you use SERIALIZABLE then you could wrap multiple SELECTs in a transaction and ensure that the underlying data isn't modified while you're reading it.
Every statement in SQL Server is run in the context of a transaction. When you do something like
select * from [dbo].[foobar];
SQL Server really does:
begin transaction;
select * from [dbo].[foobar];
commit;
So, setting an explicit transaction isolation level does affect transactions. Even the implicit ones that the database engine starts on your behalf!

Multiple stored proc call in .Net under 1 transaction

I have a .net web page in which two stored procs are called.
Both of these have begin-commit transaction in sql server.Also I am calling the second proc multiple times depending on some if conditions.
I want to wrap this whole process under a single transaction. I have looked around and found Sqltransaction and TransactionScope clases in C# will help me in this situation.
But I have never used them, always use transaction in sql server, and so do not know if the transactions in .Net will have problems as both my stored procs have their own Begin-commit transaction in Sql Server.
If they do conflict is there a way to get them to work under a single transaction?
Yes, it is possible to call (e.g. existing or legacy) Stored Procs from .Net which use manual BEGIN TRAN, COMMIT TRAN / ROLLBACKs under a .Net TransactionScope, or if you manage the transaction from a SqlTransaction. (Although to state the obvious, if you can avoid using multiple transaction technologies, do so).
For the 'happy case' scenario, what will happen is that the ##TRANCOUNT will be increased when the SPROC transactions are called (just as per nested transactions in SqlServer). Transactions are only committed when ##TRANCOUNT hits zero after the outermost commit on the connection. i.e. inner commits will simply decrease ##TRANCOUNT. Note however that the same is not true for ROLLBACKS - unless you are using SAVEPOINTS, any rollback will rollback the entire transaction. You'll need to be very careful of matching up ##TRANCOUNTs.
You sound a bit undecided about TransactionScope vs SqlTransaction. TransactionScope is more versatile, in that it can span both single phase and distributed transactions (using DTC). However, if you only need to coordinate the transaction on a single connection, same database, then SqlTransaction would also be fine.

Are there any disadvantages of placing explicit transactions in StoredProcedures in SQL SERVER 2008

I have to implement Transactions in my code. I have the following options:-
TransactionScope from C# code:- I will use this case if i have some logical code in transaction along with my Database calls.Transaction rolls back and locks are released if command timeouts are there.
Having Explicit transactions in SP:- In case of command timeout, Transactions remain opens and locks are not released.
Has any one of you faced similar issues. Please suggest. Also tell me setting XACT_ABORT_ON will help in the second case.
If your stored procedure is not an "interface procedure" (called by third-party outside your code) I recommend using TransactionScope or creating SqlTransaction in code for your procedure call. Handling transactions in code is much easier. You may read this article: http://www.code-magazine.com/Article.aspx?quickid=0305111 - as you can see if you start using transactions in stored procedures things may get unnecesarily complicated.
I always use SET XACT_ABORT ON
Server side transactions don't require MSDTC and have less overhead
TRY/CATCH makes the stored procs more reliable
See my answer here for a safe stored proc template which will deal with nested transactions too: Nested stored procedures containing TRY CATCH ROLLBACK pattern?

Sql Transaction - SQL Server or C#?

Am I right in saying that from a performance perspective, sql transactions are far better within a stored procedure than code?
At the moment I use most of my transactions in stored procs but sometimes I use code for more complex routines - which obviously I keep to a minimum as much as possible.
It's just that there was a complex routine that required too many "variables" that writing the sql transaction in c# was far easier than using SQL Server. It's a fine line between code readability and performance.
Any ideas?
The performance varies; a SqlTransaction can have less overhead than a TransactionScope, especially if the TransactionScope decides it needs to get entangled with DTC. But I wouldn't expect a vast difference between SqlTransaction and a BEGIN TRAN, except for the extra round trip. However, TransactionScope is still fast, and is the most convenient option for encapsulating multiple operations in a transaction, as the ambient transaction does not need to be manually associated with the command each time.
Perhaps a better (and more significant) factor is the isolation-level. TransactionScope defaults to the highest (serializable). Lower isolation levels allow morefor less blocking (but at the risk of non-repeatable reads, etc). IIRC a TSQL transaction defaults to one of the lower levels. But the isolation level can be tweaked for all 3 options.
I'm for TransactionScope. As per Marc, use a factory method on your TransactionScope to drop the isolation level to READ COMMITTED for most common usages I can think of.
Note that you can use both SQL transactions AND TransactionScope - the SQL BEGIN TRAN / COMMIT TRAN will have little effect on TransactionScope (other than incrementing / decrementing ##TRANCOUNT) - this way if you do need to call the same SQL Sproc elsewhere, e.g. from an adhoc query that you will still get the benefit of a transaction.
The benefit of TransactionScope IMO is that it will manage DTC for you if you DO need to do 2 phase commit (e.g. Multiple databases, Queues or other XA transactions). And with SQL 2005 and later, it works with the Lightweight Transaction Manager, so DTC won't be required e.g. if all accesses are to the one database, one connection at a time.
Transaction management in the code (read c#) is an option and to be followed for managing transactions over multiple data source or system. For managing transaction for a single database, the management at the server end will be always simpler. But if you think that the code might need to cater to a scenario where multiple data-sources get added in the transaction, keep the transactions in the code level.
I belive that the store procedure will have better performance.
If you mean you write SQL Transactions in C# and use ADO.Net or similar to execute them then they are probably less efficient because SQL will cache the query plan for a stored procedure (which is also now the case for Entity Framework - though still not as quick as proc I don't think) so really you should probably be doing it the other way round - complex procedures in SQL to get the caching benefits (if only it were that simple...)
It depends on what application.
But I will say that in most cases it is best to surprised to have logic in the database. A very advantageous also to have business logic in the databse, is that then it will be the same even if you have riders a WinForms version and a web, or whatever
But if you're talking about the CLR in SQL. The negative with this, it becomes much more difficult for a DBA to find any errors or something about. performance.

Is it possible combine transaction scope with committing a transaction inside a stored procedure?

We have a test that runs within a transaction scope. We dispose of the transaction scope at the end to avoid changing the database.
This works fine in most cases.
However, when we use Entity Framework to execute a stored procedure which contains a transaction, which is committed inside the stored procedure. We get the following error:
"Distributed transaction completed. Either enlist this session in a new transaction or the NULL transaction.\r\n "
Is it possible combine transaction scope with committing a transaction inside a stored procedure?
While you may or may not be able to solve this particular problem, I'd suggest that avoiding it entirely might be a better option. As you've seen, depending on a transaction to guarantee that your database is in a particular state doesn't always work. Also, because you are using multiple connections to the DB, you've automatically promoted any transactions that do occur to distributed transactions -- a subtle distinction, perhaps, but it changes the nature of the test. You may end up writing code to overcome the particular limitations of distributed transactions that wouldn't otherwise have been needed.
A better strategy would be -- for unit tests, anyway -- to mock out the database dependency, using in-memory mock or fake objects in place of the database. I've done something similar for LINQ to SQL (see my blog entry on the subject) For integration tests, I think you are better off using a test instance and writing set up code that reinitializes the state of the DB to known values before each test than introducing an extra transaction to clean things up. That way if your clean up code fails in a test, it won't affect other tests being run.
I use the following code inside an SP to handle contexts where a transaction may or may not be currently in force:-
DECLARE #InTran int
Set #InTran = ##TRANCOUNT
IF #InTran = 0 BEGIN TRANSACTION
/* Stuff happens */
IF #InTran = 0 AND ##TRANCOUNT > 0 COMMIT TRANSACTION
Only thing I'm not sure of is if ##TRANCOUNT reflects a transaction from a Transaction scope, its worth a shot though.

Categories