.NET transaction management without TransactionScope - c#

I was actively using TransactionScope in my application and was happy. But now I came to the situation where TransactionScope is not enough flexible: first of all, because I can't change IsolationLevel dynamically.
What is the easiest way to replace TransactionScope by database transaction which can be managed manually? One of the features I want to keep is the support of cross-connections transactions.

I think that the conculsion is that you cannot do this: Have both a distributed transaction and change the isolation level.
You cannot change the isolation level of a transaction after it has started.
To use cross connection transactons you need to use transaction scope, or a COM+ transaction via enterprise services. In both cases you cannot change the isolation level after the transaction is created.
It is possible to to change the isolation level of a SQL connection using the "set transaction isolation level " SQL statement.

Related

SQL Server management hangs when I have snapshot isolation in code

I have set my database snapshot_isolation_state_desc = ON
In c# when I start a new transaction
var dbTransaction = _myContext.Database.BeginTransaction(IsolationLevel.Snapshot);
// delete
--- break point
//insert
on the break point when I go to sql management studio and query a table it hangs until i complete the transaction. I would like to be able to see the data in the table not just hang.. But I also want to complete my c# transaction. Am I using the wrong Isolation level?
Thanks in advance
Am I using the wrong Isolation level?
Yes. The default isolation level in SSMS is READ_COMMITTED so writers (the app code) will block readers (SSMS query) unless you've turned on the READ_COMMITTED_SNAPSHOT database option. Each session can run in different isolation level and the behavior of each will depend on the chosen level of that session.
Set the desired isolation level in the SSMS query window prior to querying the table so that your query is not blocked by the uncommitted change made by the app code:
SET TRANSACTION ISOLATION LEVEL SNAPSHOT;

Why is DbContext.Database.CurrentTransaction always null?

Is there anyway to find if a DbContext is enlisted in any transaction while enlist=false in the connection string?
I was tracing DbContext.Database.CurrentTransaction, but I noticed it is always null.
I know when enlist=false, all opened connections will not enlist themselves in an ambient transaction, is that right?
If (2) is correct, how to enlist DbContext in an transaction where TransactionScope is used?
Finally, I noticed using clones of DependentTransaction with multiple DbContext and multiple threads while enlist=false will not promote the transaction to distributed one, but I wonder am I still able to commit and rollback in case an exception happened using the dependent transaction while enlist=false?
if (4) is incorrect, then is there any way to fully avoid DistributedTransaction while being able to open multiple connections with a single transaction scope?
FYI, currently Oracle database is employed; however, in future MySQL is going to be in operation as well.
Thank you
I cant really say something to 1 , 2 and 3.. But:
The distribution-thing is not absolutely clear to me. But however, MS escalates Transactions from LTM (Lightweigt Transaction Manger) to DTC if a some criteria come to play. For Example if you try to access different databases in one transaction..
The escalation from LTM to DTC or the decission if an escalation will be forced is a system-decission. Until now I didnt find a way to change this behaviour. Thats why you need to think about the transactions in general. If there is a possibility to avoid multiple-database access you may rethink your transactions.
For further information I´ll recommend Why is my TransactionScope trying to use MSDTC when used in an EF Code First app? , MSSQL Error 'The underlying provider failed on Open' and How do I use TransactionScope in C#?

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!

Should snapshot transaction isolation be avoided in client ADO.NET applications? then, what was its main purpose?

Having read in [1]:
"The persistent database connection requirement limits SNAPSHOT isolation scalability so it shouldn’t be used as a general means of implementing optimistic concurrency"
I'd like to understand:
What is persistent connection in ADO.NET?
I simply do not remember that, while programming .NET apps, I configured any persistence of SQL Server connections.
Where was it?
Is "persistent connection" antonym of disconnected mode?
What are implications of the need to have persistent connection in coding ADO.NET client apps (in, for ex., C#)?
How snapshot transaction isolation is to be used in ADO.NET client apps?
What is its (snapshot transaction isolation levels) main target area of engagement?
Also, I am somewhat confused, having read that most .NET frameworks and features, like, for ex., Linq2SQL, do not support pessimistic locking (well, hints)... but distributed transactions do not support snapshot isolation [2]
Should SQL Server built-in optimistic concurrency functionalities be avoided and substituted by custom optimistic (versioning) transaction support?
Again, what's for and why were they introduced in such (difficult to understand for me) implementation?
[1] Dan Guzman's Blog.
Concurrency Model Confusion II
http://weblogs.sqlteam.com/dang/archive/2008/06/07/Concurrency-Model-Confusion-II.aspx
[2]
Why isn't Snapshot isolation supported with Distributed Transactions in SQL Server
Why isn't Snapshot isolation supported with Distributed Transactions in SQL Server
[3]
when to prefer pessimistic model of transaction isolation over optimistic one?
when to prefer pessimistic model of transaction isolation over optimistic one?
Although the documentation is not quite clear to me, here is how I interpret it. If I have a connection in my exclusive use, I can use snapshot isolation to implement optimistic concurrency, as I described in this article: Developing Modifications that Survive Concurrency. If there is a conflict, you will get an error message: "Snapshot isolation transaction aborted due to update conflict."
However, to utilize this approach, we must keep the connection open, and we cannot use connection pooling. Note that in some environments idle connections are automatically killed after some idle period.

Transactionscope or IDbTransaction

EDIT: I am only accessing 1 database but a DTC transaction IS being created. I can see it in the DTC Transaction List GUI (Component services plugin). Why is this?
I need to perform a transaction (several insert commands) against 1 database.
Should I be using TransactionScope or IDbTransaction (connection.BeginTransaction)?
TransactionScope seems to be newer … but should it just be used for 2-phase commits?
Thanks
TransactionScope will only escalate to a distributed transaction if it detects more than one connection. This means that TransactionScope is just as lightweight as BeginTransaction for local transactions and TransactionScope is a lot easier to use.
As long as you use a single connection and do not close and re-open during a TransactionScope, it should not promote to a distributed transaction. If you do not have the DTC service running on your machine it will throw an exception if it tries to promote. If the DTC running, you will be non the wiser of the promotion except for a slight pause.
In the vast majority of cases, TransactionScope is much nicer to use, especially in conjunction with "using" blocks.
Be careful though, if you use SQL Server 2000. It does not play well with TransactionScope and will always escalate to a distributed transaction.
See this link for some details.

Categories