C# WCT ReleaseServiceInstanceOnTransactionComplete - c#

Im sure I understand those properties all wrong.
ReleaseServiceInstanceOnTransactionComplete -
from what I understood- dispose your service instance that you initilize in your client after your transaction there complete\abord.
2.TransactionAutoComplete-
from what I understood- dispose your service instance that you initilize in your client after the opertion ended.
My english isnt that good so after reading each article, I still have peaces that i dont understand.
Thank you for your time.
EDIT 1:
Can you expline to me in simple english what is the purpose of InstanceContext?
I have read this article : http://www.danrigsby.com/blog/index.php/2008/05/23/understanding-instancecontext-in-wcf/
and still didnt get it.

ReleaseServiceInstanceOnTransactionComplete really disposes your service instance when the transaction is completed or aborted. It is by default set to true. If you for example create service with PerSession instancing you may want to turn it to false to ensure your session lifetime instead of transaction lifetime.
TransactionAutoComplete has nothing to do with disposing. It just controls transaction used to execute operation. If TransactionScopeRequired is set to true, your operation will be automatically wrapped by TransactionScope. TransactionAutoComplete set to true will than automatically commit transaction if the operation executes without error or rollback transaction if operation throws exception. If you set TransactionAutoComplete to false you will have to commit transaction manually by calling:
OperationContext.Current.SetTransactionComplete();

Related

SQL Server - Registering user code to be run when a transaction completion is attempted

I have an SQL CLR Stored Procedure which registers an event handler for the completion of the current transaction
[SqlProcedure]
public static void MySProc()
{
Transaction.Current.TransactionCompleted += new TransactionCompletedEventHandler(Current_TransactionCompleted);
Transaction.Current.Rollback(); //###Current_TransactionCompleted called here###
}
static void Current_TransactionCompleted(object sender, TransactionEventArgs e)
{
}
And this is the T-SQL calling code
begin tran
EXEC [dbo].[MySProc]
commit tran
This works as expected: after the call "Transaction.Current.Rollback()" the handler gets executed.
However, what I am interested in is to have my handler (or my code anyway) called when the transaction is completed OUTSIDE of "MySProc()".
[SqlProcedure]
public static void MySProc()
{
Transaction.Current.TransactionCompleted += new TransactionCompletedEventHandler(Current_TransactionCompleted);
}
static void Current_TransactionCompleted(object sender, TransactionEventArgs e)
{
}
begin tran
EXEC [dbo].[MySProc]
rollback tran -- ###Current_TransactionCompleted NOT called###
Is this possible?
If not, is there another way I can hook the current transaction completion and have the "last word" on the transaction outcome (commit or rollback)?
what I am interested in is to have my handler called when the transaction is completed OUTSIDE of "MySProc()". ... Is this possible?
At first I thought that this was not possible at all, but upon further investigation I found that it is (to a degree). In my first test I was printing out a message via SqlContext.Pipe.Send("..."); when the handler was called. This never displayed anything so I thought that it wasn't being called. However, I had forgotten that there would be no active SqlContext within the event handler as it's disconnected from the calling process (i.e. the session that initiated it). Once I switched to using File.WriteAllText() in the event handler, then I could see that it was indeed being called upon execution of COMMIT or ROLLBACK outside of any SQLCLR code. And, I was using Transaction.Current.TransactionCompleted just like in the question, and not using a static variable as I was also originally testing with. And, there is no need to enlist anything since we are referencing the "current" transaction (i.e. it's already enlisted on the context connection).
HOWEVER, I should clarify that the "this" that works is merely having some SQLCLR code get executed after a transaction has either committed or rolled-back. And I even tested by killing a session after beginning a transaction and registering the handler, and it did trigger the event handler.
BUT, there is a slight disconnect between the wording of most of the question and the wording of the title of this question (emphasis added) — Registering user code to be run when a transaction completion is attempted — as they seem to indicate different timings of when the handler should run.
If not, is there another way I can hook the current transaction completion and have the "last word" on the transaction outcome (commit or rollback)?
I would say: Not only "no" (at least not that I'm aware of), but that you wouldn't want it to be "yes" because transactions can be killed for a variety of reasons (deadlocks, session killed, connection pooling executing sp_reset_connection, batch-level errors, any error if XACT_ABORT is ON, etc) and you really don't want to interfere with SQL Server's ability to manage transactions.
The closest you can come, as far as I know, is creating SQLCLR methods for "commit" and "rollback" (similar to your example code in the question) and require that your uses only use those SQLCLR stored procedures for doing those actions.
The event handler in SQLCLR runs after the COMMIT or ROLLBACK. This is not an event handler that is trigger by the request to end the transaction, but by the transaction actually completing. At that point, there is no way to "have the last word on it".

InvalidOperationException when calling Transaction.Current

I have code inside my DatabaseClient class that checks whether or not there is an active TransactionScope by examining the ThreadStatic property, Transaction.Current:
if (Transaction.Current == null)
{
// open a new connection and do things
}
I have code consuming this class that creates a TransactionScope, executes two database operations, and then completes it. The application then moves on to do further database work. But now when it calls the code above, I get an exception:
System.InvalidOperationException: The current TransactionScope is already complete.
What do I need to do in order to "reset" the current transaction so that I can check Transaction.Current safely again?
When a transaction has been rolled back or committed, it can't be reused. Create a new one.
Some sample code here:
https://learn.microsoft.com/en-gb/dotnet/api/system.transactions.transactionscope?view=netframework-4.7

Why does this TransactionScope not block subsequent requests until the first request is done?

I've been looking into transactions for two days now, perhaps I'm missing something obvious after taking in so much information. The goal here is to block simultaneous request. If condition is true, data is inserted, after which condition will be false. Simultaneous requests will both check condition before data could be inserted and then will both try to insert data.
public async Task<ActionResult> Foo(Guid ID)
{
Debug.WriteLine("entering transaction scope");
using (var transaction = new TransactionScope(
TransactionScopeOption.Required,
new TransactionOptions() { IsolationLevel = IsolationLevel.Serializable },
TransactionScopeAsyncFlowOption.Enabled
))
{
Debug.WriteLine("entered transaction scope");
var context = new DbContext();
Debug.WriteLine("querying");
var foo = context.Foos.FirstOrDefault(/* condition */);
Debug.WriteLine("done querying");
context.Foos.Add(new Foo());
/* async work here */
Debug.WriteLine("saving");
context.SaveChanges();
Debug.WriteLine("saved");
Debug.WriteLine("exiting transaction scope");
transaction.Complete();
Debug.WriteLine("exited transaction scope");
return View();
}
}
This is the debug output when executing two requests at once with Fiddler:
entering transaction scope
entered transaction scope
querying
done querying
entering transaction scope
entered transaction scope
querying
done querying
saving
saving
saved
A first chance exception of type 'System.Data.SqlClient.SqlException' occurred in System.Data.dll
exiting transaction scope
exited transaction scope
This is my understanding of how the code is supposed to work:
A transaction with serialisable isolation level is required, to prevent phantom reads.
I cannot use DbContext.Database.Connection.BeginTransaction, when executing a query an error is thrown:
ExecuteReader requires the command to have a transaction when the connection assigned to the command is in a pending local transaction.
The Transaction property of the command has not been initialized.
TransactionScope normally causes causes problems when using task-based async.
http://entityframework.codeplex.com/discussions/429215
However, .NET 4.5.1 adds additional constructors to deal with this.
How to dispose TransactionScope in cancelable async/await?
EF 6 has improved transaction support, but I'm still on EF 5.
http://msdn.microsoft.com/en-us/data/dn456843.aspx
So obviously it's not working like I want it to. Is it possible to achieve my goal using TransactionScope? Or will I have to do an upgrade to EF 6?
Serialization does not solve the old 'check then insert' problem. Two serializable transactions can concurrently evaluate the condition, conclude that they have to insert, then both attempt to insert only for one to fail and one to succeed. This is because all reads are compatible with each other under serializable isolation (in fact they are compatible under all isolation levels).
There are many schools of thought how to solve this problem. Some recommend using MERGE. Some recommend using a lock hint in the check query to acquire an X or U lock instead. Personally I recommend always INSERT and gracefully recover the duplicate key violation. Another approach that does work is using explicit app locks.
EF or System.Transactions really add only noise tot he question. This is fundamentally a back-end SQL problem. As for the problem of how to flow a transaction scope between threads see Get TransactionScope to work with async / await (obviously, you already know this, from reading the OP... I didn't register in first read). You will need this to get your async code to enlist in the proper context, but the blocking/locking is fundamental back end problem, still.

WCF Distributed Transaction Flow using normal return values

I have got a service that should use distributed transactions.
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
public bool ServiceMethod(int parameterPlaceHolder)
{
return SomeOperationResult();
}
For reasons out of my responsibility, this service should never throw faults. On success it returns one value, on failure another (abstracted to a bool here for demo purposes).
The transaction flowing works.
However, the attribute implies that any result that is not an uncaught exception will complete the transaction. That's not the behavior I want. I want to control the outcome of the transaction myself. On returning false, I want to have the transaction fail.
I have tried various methods:
The obvious one: setting TransactionAutoComplete to false. This means that I have to use a session based service. I don't want to. I don't need to. I'm perfectly fine with a single transaction scope per call. But it's not allowed. ("TransactionAutoComplete set to false requires the use of InstanceContextMode.PerSession.")
The DIY one: setting TransactionScopeRequired to false and using my own. This means the flowed transactions no longer work and I create a new local transaction every time.
The desperate one: Trying to get hold of the transaction that WCF creates and rolling it back on my own... this leads to my service throwing exceptions because it tries to AutoComplete a transaction that is long gone.
I'm out of ideas. Does anyone know how to create my own transaction scope, using a flowed distributed transaction, not using the Microsoft AutoComplete-On-Normal-Return pattern? I would like to not complete the transaction without throwing an exception.
Transaction scopes can nest. The entire transaction aborts if you don't completed any scope. So:
using (new TransactionScope()); //Doom transaction
Better comment this line.
You can also try to call stuff on Transaction.Current but I have no experience with that.

Wcf transaction

Is there a way to know in a wcf operation that a transaction has committed?
Ok, second attempt into being more specific.
I got a WCF service with an Operation with Transaction flow allow.
Now when a client call my wcf service it can have a transaction. But my service is also interested in the fact that the transaction on the client has succeeded. Because on my wcf service level, if everything went well. It has other things to do, but only if all transactions has been committed....
Is there like an event I can subscribe to or something?
It depends on the service itself and how you are handling transactions. If you are engaging in transactions in WCF through WS-Transaction then if the call to the client succeeds without exception, you can assume the transaction took place.
However, if this is in the context of another transaction, then you can't be sure if the transaction went through until the containing transaction is completed.
Even if you are using the TransactionScope class, if you have the service enabled to use transactions, you still have to take into account the encompassing transaction (if there is one).
You will have to provide more information about where the transaction is in relation to the call in order for a more complete answer.
Try using the operation behavior attribute above, in your operation that allows TransactionFlow:
[OperationBehavior(TransactionScopeRequired=true)]
If a transaction flows from the client, then the service will use it.
bool isTransactionComplete = true;
try
{
using (TransactionScope trScope = new TransactionScope(TransactionScopeOption.Required))
{
//some work
trScope.Complete();
}
}
catch (TransactionAbortedException e)
{
//Transaction holder got exception from some service
//and canceled transaction
isTransactionComplete = false;
}
catch//other exception
{
isTransactionComplete = false;
throw;
}
if (isTransactionComplete)
{
//Success
}
As casperOne wrote it depends on the settings. But you should be aware of complex transactions like
1) session service and simultaneous transactions for one service instance
2) transaction inside transaction

Categories

Resources