Did you know: Find out if an exception was thrown from a finally block!
This is a big biggie for me, because it enables a much nicer syntax for a lot of stuff. But first, let us show this:
using(new ExceptionDetector()) { if(new Random().Next(1,10)%2 == 0) throw new Exception(); }
How can you tell, from the ExceptionDetector, if an exception was thrown or not? Well, conventional wisdom, and what I thought about until 15 minutes ago, says that you can't. I want to thank Daniel Fortunov, for teaching me this trick:
public class ExceptionDetector : IDisposable { public void Dispose() { if (Marshal.GetExceptionCode()==0) Console.WriteLine("Completed Successfully!"); else Console.WriteLine("Exception!"); } }Amazing!
Comments
Ooooo nice one.
That puts:
using(new UnitOfWork()) {} back on the table - instead of Within.UnitOfWork(anon delegate etc).
That's some sweet stuff :) how come I haven't heard of it until now!
Ok,
What am i missing? In what scenario is this needed?
Oh neat! Mind that I think it can be fooled... What happens if your using statement appears within an exception handler? Will you be able to tell whether the exception occurred within or outside the exception handler?
Doh! Meant within the using statement.
@inoodle,
Absolutely!
@Derik,
It enable this code:
using(new Transaction())
{
/// blah
}
Where the transaction can decide if it wants to commit or rollback based on the exception status.
Derik,
Previously to do a "using" with a transaction you had to do something like this:
using(Transaction t = new Transaction()){
//important stuff
t.VoteToCommit();
}
Using this technique it looks like you don't need the VoteToCommit() call, which is good because I always forgot to add that, and then spent hours debugging why my tables weren't being updated.
Then why don't you write the code in the post using the use case, in the first place?
There's so much going on in comments on this site, all the time, and you write 187 posts a day, average. When wife and kid start to complain, I'll give them your e-mail :)
@Jeff Perrin,
Yep, I've forgotten Transaction commits too. Wouldn't it be nice to have Smalltalk blocks in C# instead of these clunky control structures? Anonymous delegates and even lambdas don't quite cut it. Oh well.
Still, the exception detecting using trick will be very useful indeed... My only concern is that its behavior may surprising to the unwary.
One last thought...
Is this hack portable? What will Mono do? I assume it doesn't necessarily implement its exception handling using Win32 SEH. Therefore I wouldn't be too surprised if the Marshal.GetExceptionCode() method were unimplemented or not meaningful...
ayende, this is what MSDN lib says:
GetExceptionCode is exposed for complier support of structured exception handling (SEH) only. If called before an exception is thrown, this method returns 0xCCCCCCCC.
Ok, so firstly, == 0 seems to be wrong. Then, which exception? The last exception on this thread? The innermost exception in the current scope? The exception in the innermost scope? You'd have to make and test a whole lot of assumptions here.
What about portability? Mono?
I have to say I'd rather write VoteToCommit() than build on such weak assumptions...
I'm a bit slow to catch on as well. Is the intention of ExceptionDectector to allow you to dispose differently depending on wether or not an exception was thrown? I would need a use case to more fully understand this feature.
@inoodle:
I would contend that Within.UnitOfWork is a much clearer syntax, even with delegate { } tacked on the end.
@Jeff Perrin:
What if you do not want to implicitly commit the transaction if no exception is thrown? The current functionality of SqlTransaction is to implicitly Rollback if no Commit was issued. I would prefer more descriptive syntaxt like using(Transaction trans = new Transaction(CommitStyle.Implicit)) { }
What is my feeble brain missing?
You aren't missing much. That is the intent of the code.
The ideas behind With.Transaction, etc are to handle the case of thrown exception, so having a good way to handle that is very nice.
I wonder why Microsoft did not use this for System.Transactions? Do they know something we don't? Did they simply not think of it? Or, did they discard it because they felt that a more explicit syntax was easier for us to understand?
I would go with the more explicit syntax reason
Provided this is reliable (something I'm not yet convinced of), this is going to simplify a whole bunch of things in my codebase.
Interestingly, you could do this in a supported way in Delphi, and I was talking to a friend recently to see if it was possible in .NET.
I wonder, does referencing Marshal introduce any odd dependencies that wouldn't normally be present? If so, this could explain why MS didn't use it themselves in System.Transactions.
Ah, just spotted a potential issue for me at least...
Marshal.GetExceptionCode() won't work under asp.net medium trust.
Back to delegates :)
Comment preview