How can I add nested transactions in NHibernate? - c#

I have a use case where I am processing multiple configuration within a function, each configuration processing runs within a separate transaction and transaction gets commited if everything is fine, now if at all anything goes wrong in processing of further configuration I want to revert all the commuted transaction. Can anyone please help me with code snippet? My application is on .net.

To the best of my knowledge, NH doesn't support nested transactions.
You can use a transaction at the root of your use case, or at any point along the way, but it's all or nothing, AFAIK.

It's not a matter of using nested transactions. It's a matter of ensuring that you have a transaction that surrounds all the relevant code - so it should be open/closed "higher up". Each individual section should then either not care about transactions at all, or it should "piggy-back" on any existing transaction and only open a new transaction when one does not already exist.
As a guideline, transaction management is an overall concern that should be handled in different sorts of wrapper methods and applied as needed by the application - not hidden away in specific low-level support routines.

Related

Is TransactionScope in .NET applicable to only specific types of transactions?

TransactionScope provides functionality to convert a set of operations into a transaction so that either all are completed successfully or none. My question is does transaction scope apply to certain types of operations (e.g. only SQL connnections, Azure, etc.)?
For example, consider the code block below
using (TransactionScope scope = new TransactionScope())
{
SaveToSQLserver(parameter);
SaveToSalesForce(parameter);
SaveToSAP(parameter);
SaveToAzure(parameter);
scope.Complete();
}
Now suppose an error occurs at SaveToSAP, where it has already saved to Salesforce; how is the transaction going to rollback from Salesforce? And if everything is in memory, then how is it going to make sure that when it actually saves it will succeed?
A TransactionScope is capable of supporting a distributed transaction across many different types of systems, but it is not an automatic thing. This documentation provides a glimpse into that (it's worth checking out the whole of the document hierarchy on Transaction Processing.
As mentioned by Dave Anderson in the comments, a Resource Manager controls what is done during commit and rollback, so the "how is it done" is governed individually by each resource manager.
So, can other things participate in a transaction scope besides just SQL? Yes, they can. As long as a Resource Manager exists for each system (e.g. a messaging system), it can participate (enlist).
If you are working with something that can't enlist, you have to manually do a compensating transaction when you detect you need to rollback (usually when an exception occurs).

What is the benefit/usage of TransactionScope?

I have been using NHibernate for a while and came across the code below that uses a Transaction scope.
using (var scope = new TransactionScope(TransactionScopeOption.Required))
{
using (var session = sessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
// do work
}
}
}
I generally do everyting without wrapping the code to a TransactionScope Am I doing something wrong or am I just missing out some beautiful functionality ?
The usage is: transactions. Whether that is benefit is more complex. There are more direct ways of achieving transactions - ADO.NET transactions. These are a little awkward to work with (you need to remember to set the transaction on every command), but are very efficient.
Transaction scope has the advantage of an ambient transaction; this makes it easier to work with. However, it works in a different way. In particular, transaction-scope supports multiple resource transactions - which can mean multiple databases etc. This is typically done via DTC, but DTC has overheads - it is more expensive (and requires specific firewall configuration, etc). In many single-database cases it can short-cut and use the LTM instead of full DTC, but this is still more expensive than ADO.NET transactions... just not as expensive as DTC.
A powerful feature, but make sure you intend to use it before you do ;p
If you are not using any TransactionScope explicitly, every statement you execute on the database will run in a separate transaction.
With a TransactionScope you can bundle multiple statements into a big transaction and undo everything as a block.
This is necessary, when updating multiple tables in multiple statements but performing virtually one big thing, that has to work or not be done at all.
You are missing out on some beautiful functionality: with the transaction scope in place, the code with the transaction scope will participate in ambient transaction if invoked from inside a piece of code running in its own transaction scope. Without transaction scope, your code will have its own transaction (from the deepest nested block) which could fail without failing the outer transaction.
In addition, anything inside your // do work block would have easier time participating in your transaction if you put a transaction scope outside it. The code would be able to break your transaction without having to propagate the error code up the chain or throwing an exception, which could potentially be ignored by code in the middle.
Note: Don't forget to call scope.Complete() on the transaction scope before the end of its using block.
Are you using transactions at all. If you aren't, then you should be.
I personally don't use TransactionScope in any of my NHibernate, but then again I don't need it. In a web environment with a single database, it's not really necessary. my unit of work is a web request. I open my connection on BeginRequest and close it on EndRequest. I use a generic repository that will start a transation if none is present and defined a TransactionAttribute that decorates controller actions so that all table updates are performed in a single transaction.
TransactionScope is just Microsoft's generic way to put all transactional aware resources into a single transaction. This may be multiple databases, a transactional file system, .... Things to worry about in these scenarios is that the transaction most likely will be promoted to the DTC to coordinate all the updates.
Also, I don't know if it's still an issue, but older versions of NHibernate used to have a memory leak associated with using TransactionScope.
Here's a link with everything you need to know about TransactionScopes http://www.codeproject.com/Articles/690136/All-About-TransactionScope

Transactions should be handled in .NET or SQL Server?

I got into an application which is using .NET/C# as front end and SQL Server 2008 as back end. I found that ALWAYS transactions are handled in the c# code. Seems its an unwritten rule for this project that we shouldn't use Transactions within stored procedure.
I personally feel that transactions should be handled within Stored Procedure as it would give more control over the code! We might have lot of validations happening within the script all this while we don't need a open transaction. We need to open a transaction just before we do a Insert/Update/Delete and can close it asap.
Looking for answers which would help me understand the best practice for handling transactions and when exactly do we need to opt for Transactions within Stored Proc / C#.
There isn't a hard and fast rule, but I see several reasons to control transactions from the business tier:
Communication across data store boundaries. Transactions don't have to be against a RDBMS; they can be against a variety of entities.
The ability to rollback/commit transactions based on business logic that may not be available to the particular stored procedure you are calling.
The ability to invoke an arbitrary set of queries within a single transaction. This also eliminates the need to worry about transaction count.
Personal preference: c# has a more elegant structure for declaring transactions: a using block. By comparison, I've always found transactions inside stored procedures to be cumbersome when jumping to rollback/commit.
We might have lot of validations happening within the script all this
while we don't need a open transaction. We need to open a transaction
just before we do a Insert/Update/Delete and can close it asap.
This may or may not be a problem depending on how many transactions are being opened (it's not clear if this is a single job, or a procedure which is run with high concurrency). I would suggest looking at what locks are being placed on objects, and how long those locks are being held.
Keep in mind that validation possibly should lock; what if the data changes between the time you validated it and the time the action occurs?
If it is a problem, you could break the offending procedure into two procedures, and call one from outside of a TransactionScope.

What to do when neither TransactionScope nor nested transactions are supported?

TransactionScope is an amazing feature but too few providers do implement it correctly.
I don't want to pass the connection as parameter.
Not sure what you wanted to achieve using TransactionScope here - if idea is to have auotmatic flow of transactions across methods (and simple enlistment within ongoing transaction) then passing connection as parameter is not the only way. You can pass current connection and transaction using current CallContext (or Current Thread). Put a simple static wrapper that would check if connection/transaction exists on current call context and creates if not. This is transparent non-intrusive way as opposed to passing by parameter.
Now, if you are looking at flowing transactions across app domain boundaries and/or using multiple resource managers (i.e. using distributed transactions) then the best bet would be to use TransactionScope and roll out your own ResourceManager. Of course, this is not a trivial thing but then that's what the requirement entails. If underlying system does not provide transactional resource then custom resource manager can use compensating transaction for having roll backs (for example, a manager on a top of file system can use "Delete Folder" as compensating transaction against original transaction of "Create Folder").
Maybe you're looking for System Prevalence.
Basically, every transaction is journaled (the details of the transaction are saved) and if the application crashes and is restarted you can either pick up where you left off or rollback the changes based on the journaled state.
Here's a link to the Snapshot pattern that can aid you in implementing System Prevalence.

Can a Snapshot transaction fail and only partially commit in a TransactionScope?

Greetings
I stumbled onto a problem today that seems sort of impossible to me, but its happening...I'm calling some database code in c# that looks something like this:
using(var tran = MyDataLayer.Transaction())
{
MyDataLayer.ExecSproc(new SprocTheFirst(arg1, arg2));
MyDataLayer.CallSomethingThatEventuallyDoesLinqToSql(arg1, argEtc);
tran.Commit();
}
I've simplified this a bit for posting, but whats going on is MyDataLayer.Transaction() makes a TransactionScope with the IsolationLevel set to Snapshot and TransactionScopeOption set to Required. This code gets called hundreds of times a day, and almost always works perfectly. However after reviewing some data I discovered there are a handful of records created by "SprocTheFirst" but no corresponding data from "CallSomethingThatEventuallyDoesLinqToSql". The only way that records should exist in the tables I'm looking at is from SprocTheFirst, and its only ever called in this one function, so if its called and succeeded then I would expect CallSomethingThatEventuallyDoesLinqToSql would get called and succeed because its all in the same TransactionScope. Its theoretically possible that some other dev mucked around in the DB, but I don't think they have. We also log all exceptions, and I can find nothing unusual happening around the time that the records from SprocTheFirst were created.
So, is it possible that a transaction, or more properly a declarative TransactionScope, with Snapshot isolation level can fail somehow and only partially commit?
We have spotted the same issue. I have recreated it here - https://github.com/DavidBetteridge/MSMQStressTest
For us we see the issue when reading from the queue rather than writing to it. Our solution was to change the isolation level of the first read in the subscriber to be serialised.
no, but snapshot isolation level isn't the same as serializable.
snapshoted rows are stored in the tempdb until the row commits.
so some other transaction can read the old data just fine.
at least that's how i understood your problem. if not please provide more info like a grapf of the timeline or something similar.
Can you verify that CallSomethingThatEventuallyDoesLinqToSQL is using the same Connection as the first call? Does the second call read data that the first filed into the db... and if it is unable to "see" that data would cause the second to skip a few steps and not do it's job?
Just because you have it wrapped in a .NET transaction doesn't mean the data as seen in the db is the same between connections. You could for instance have connections to two different databases and want to rollback both if one failed, or file data to a DB and post a message to MSMQ... if MSMQ operation failed it would roll back the DB operation too. .NET transaction would take care of this multi-technology feature for you.
I do remember a problem in early versions of ADO.NET (maybe 3.0) where the pooled connection code would allocate a new db connection rather than use the current one when a .NET level TransactionScope was used. I believe it was fully implemented with 3.5 (I may have my versions wrong.. might be 3.5 and 3.5.1). It could also be caused by the MyDataLayer and how it allocates connections.
Use SQL Profiler to trace these operations and make sure the work is being done on the same spid.
It sounds like your connection may not be enlisted in the transaction. When do you create your connectiion object? If it occurs before the TransactionScope then it will not be enlisted in the transaction.

Categories

Resources