Scope and Dispose of Transactions

Arnošt Löbel added an important comment on the sample code that I published for refreshing a view:

It is for the best if all transaction objects are always properly scoped, e.g. within a C# 'using' block. This includes transactions, sub-transactions, transaction groups, and other scope-like objects such as StairsEditScope. Not doing so can yield some pretty weird situations when exceptions come into play.

In your example, proper scoping can be implemented by adding 'using' statements, which automatically dispose of the objects when they leave the scope, like this:

  using( TransactionGroup group 
    = new TransactionGroup( doc ) )
  {
    group.Start( "Muda a fachada" );
 
    using( Transaction tran 
      = new Transaction( doc ) )
    {
      tran.Start( "Step 1 " );
      Calcula_Padrao( doc, Paneis, 0.10 );
      tran.Commit();
 
      uidoc.RefreshActiveView();
 
      Thread.Sleep( 2000 );
 
      tran.Start( "Step 2 " );
      Calcula_Padrao( doc, Paneis, 0.4 );
      tran.Commit();
 
      uidoc.RefreshActiveView();
 
      Thread.Sleep( 2000 );
 
      tran.Start( "Step 3 " );
      Calcula_Padrao( doc, Paneis, 0.05 );
      tran.Commit();
 
      uidoc.RefreshActiveView();
 
      Thread.Sleep( 2000 );
 
      tran.Start( "Step 4 " );
      Calcula_Padrao( doc, Paneis, 0.4 );
      tran.Commit();
 
      uidoc.RefreshActiveView();
    }
    group.Assimilate();
  }

This is the safest way to guarantee that scoped objects are destroyed in the proper order. In this case, the transaction must be destroyed (and committed or rolled back) before the group is finished. If not, an exception will be thrown, and since it would be thrown during garbage collection, the consequences are hard to predict.

Here is FachadaCinetica2.zip containing an updated version of the kinetic facade sample command with proper scoping and including the complete Revit 2013 source code, Visual Studio solution and add-in manifest.

Oh, and by the way, the final result of the kinetic facade sample up and running can be viewed on Prof. José Luis Menegotto's Kinetics Architecture page for both AutoCAD and Revit.

Question: How is it if I have a normal external command and just start and commit a transaction within the command Execute method?

Does that require me to add a 'using' block around it as well, or does it get cleaned up properly by itself automatically?

Answer: Basically, the same rules apply. I recommend always scoping inside 'using' blocks.

Again, it makes no difference if no unhandled exceptions are thrown during your command, but if they are, it might trigger some harsh treatment from the API firewall:

In addition, the API may start providing more of these scope-like objects in the future, and it is better get into habit of scoping them properly in your C# and VB code, something provided automatically and for free in C++.