How do I check for missing scope.Complete() statements?
Asked Answered
G

3

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.

Gene answered 18/6, 2012 at 14:20 Comment(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.Wedded
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.Smythe
@Smythe That works as a possible solution! It looks like NDepend can do this (ndepend.com/Features.aspx#CQL) and free alternatives exist (e.g., reflectoraddins.codeplex.com/…). Could you post your comment as a possible answer to this question? Thanks!Gene
@BenPaul done, and retagged - I'd wait a bit and see if Patrick from NDepend can add more, he would be the best person to clarify if this is a job for NDependSmythe
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.Gene
G
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.

Ganda answered 19/6, 2012 at 12:31 Comment(0)
G
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 */);
Glabrate answered 18/6, 2012 at 14:26 Comment(1)
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...Gene
G
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.

Ganda answered 19/6, 2012 at 12:31 Comment(0)
T
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.

Timothea answered 19/6, 2012 at 14:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.