5

Programmers on my team sometimes open a transaction and forget to include the scope.Complete() statement (see code block below). Any ideas on ways to either

  1. Search our solution for missing scope.Complete() statements, or

  2. Have Visual Studio automatically highlight or raise a warning for missing scope.Complete() statements?

Here's the line we miss:

 using(TransactionScope scope = new TransactionScope())
 {
      /* Perform transactional work here */
      scope.Complete(); <-- we forget this line
      /* Optionally, include a return statement */
 }

What I have tried
 
I have tried using a ReSharper Custom Pattern for this purpose, with no luck. Ideally I would search for something like:

using(TransactionScope scope = new TransactionScope())
{
    $statements1$
    [^(scope.Complete();)]
    $statements2$
}

However, ReSharper only accepts regular expressions for identifiers, not for statements, so this does not appear to work (http://www.jetbrains.com/resharper/webhelp/Reference__Search_with_Pattern.html).

Any ideas? I'm open to using other plugins or tools as well.

bad_coder
  • 11,289
  • 20
  • 44
  • 72
Ben Paul
  • 118
  • 1
  • 5
  • I've seen this done before with a test. Via reflection you can determine if a method is called on an instance. If it's not then the test fails. – Chuck Conway Jun 18 '12 at 14:30
  • 1
    I reckon with NDepend you could set up a rule to look for methods where the number of `TransactionScope` ctor usages was less than the number of `Complete` usages. – AakashM Jun 18 '12 at 15:04
  • @AakashM That works as a possible solution! It looks like NDepend can do this (http://www.ndepend.com/Features.aspx#CQL) and free alternatives exist (e.g., http://reflectoraddins.codeplex.com/wikipage?title=CodeSearch&referringTitle=Home). Could you post your comment as a possible answer to this question? Thanks! – Ben Paul Jun 18 '12 at 16:09
  • 1
    @BenPaul done, and retagged - I'd wait a bit and see if [Patrick from NDepend](http://stackoverflow.com/users/27194/patrick-from-ndepend-team) can add more, he would be the best person to clarify if this is a job for NDepend – AakashM Jun 18 '12 at 16:36
  • Correction to my comment above: reflector is not free. So far I haven't found any free code search tools that would fit this purpose. – Ben Paul Jun 19 '12 at 21:25

3 Answers3

4

Could you force programmers to use a custom API instead of the low-level scope.Complete stuff?

A closure will force usage of .Complete():

public static void Do(this TransactionScope scope, Action action) {
  using (scope) {
    action();
    scope.Complete();
  }
}

Then you could do:

new TransactionScope().Do(() => /* Transactional stuff */);
flq
  • 22,247
  • 8
  • 55
  • 77
  • Thanks for the answer! That's true, we could refactor our existing transactions to use a custom API similar to the one you've written, and then try to force programmers to use this API for future code. I think the tough point would be refactoring all transactions, since we have a large code base already. I'm not sure if that would be worth the time involved. Thinking about it... – Ben Paul Jun 18 '12 at 16:26
3

NDepend can certainly help, but cannot check 100% of what you are asking for. NDepend doesn't know about the method body internals (order of method calls). So at best, you can write a code rule over LINQ (CQLinq) that will check that if a method is creating a TransactionScope, at least it must call TransactionScope.Complete():

warnif count > 0
from m in Application.Methods
where m.CreateA("System.Transactions.TransactionScope") &&
     !m.IsUsing("System.Transactions.TransactionScope.Complete()")
select m

Note that if developers are disciplined enough to avoid creating multiple TransactionScope in one method, this rule should work for you.

Patrick from NDepend team
  • 13,237
  • 6
  • 61
  • 92
0

I'm not aware of any existing R# plugin that checks for this, but you could certainly create one of your own. All you'd have to do is to detect a using statement with a variable declaration of the TransactionScope type, then iterate the contained statements looking for the Complete() call.

If you are interested in doing this, I recommend you download the ReSharper SDK and check out the Plugin Development Guide.

Rais Alam
  • 6,970
  • 12
  • 53
  • 84
Dmitri Nesteruk
  • 23,067
  • 22
  • 97
  • 166