The ITransactionFinalizer interface of Revit API can be implemented to do something after a Transaction has been committed or rolled back.
It provides two methods to be implemented, the OnCommitted() and the OnRolledBack(). The former will be called when the transaction of concern has been committed and the latter rolled back.
Here is a sample implementation:
#region Namespaces
using System;
using System.Text;
using System.Linq;
using System.Xml;
using System.Reflection;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media.Imaging;
using System.Windows.Forms;
using System.IO;
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Events;
using Autodesk.Revit.DB.Architecture;
using Autodesk.Revit.DB.Structure;
using Autodesk.Revit.DB.Mechanical;
using Autodesk.Revit.DB.Electrical;
using Autodesk.Revit.DB.Plumbing;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using Autodesk.Revit.UI.Events;
using Autodesk.Revit.Collections;
using Autodesk.Revit.Exceptions;
using Autodesk.Revit.Utility;
using RvtApplication = Autodesk.Revit.ApplicationServices.Application;
using RvtDocument = Autodesk.Revit.DB.Document;
#endregion
namespace RevitAddinCSProject
{
public class SampleTransactionFinalizer : ITransactionFinalizer
{
#region ITransactionFinalizer Members
public void OnCommitted(Document document, string strTransactionName)
{
string msg = string.Format("Transaction {0} on document {1} has been committed.", strTransactionName, document.Title);
TaskDialog.Show(this.ToString(), msg);
}
public void OnRolledBack(Document document, string strTransactionName)
{
string msg = string.Format("Transaction {0} on document {1} has been rolled back.", strTransactionName, document.Title);
TaskDialog.Show(this.ToString(), msg);
}
#endregion
}
}
If the ITransactionFinalizer implementation is linked with a Transaction through the FailureHandlingOptions like the following:
Transaction trans = new Transaction(CachedDoc, "SampleTransactionFinalizer Test");
trans.Start();
// Do something with the trans.
// ...
//FailureHandlingOptions fhOpts = new FailureHandlingOptions(); //ERROR: The type 'Autodesk.Revit.DB.FailureHandlingOptions' has no constructors defined
FailureHandlingOptions fhOpts = trans.GetFailureHandlingOptions();
fhOpts.SetTransactionFinalizer(new SampleTransactionFinalizer());
trans.Commit(fhOpts);
and the code is called by an external command, the following TaskDialog will appear:
Similar code to test the rollback scenario:
Transaction trans1 = new Transaction(CachedDoc, "SampleTransactionFinalizer Test1");
trans1.Start();
// Do something with the trans1.
// ...
//FailureHandlingOptions fhOpts1 = new FailureHandlingOptions(); //ERROR: The type 'Autodesk.Revit.DB.FailureHandlingOptions' has no constructors defined
FailureHandlingOptions fhOpts1 = trans1.GetFailureHandlingOptions();
fhOpts1.SetTransactionFinalizer(new SampleTransactionFinalizer());
trans1.RollBack(fhOpts1);
and similar output TaskDialog:
As can be seen, things are pretty straightforward about the ITransactionFinalizer interface implementation and we would not talk much about it, but it may be worth of a few words about how to hook up the ITransactionFinalizer implementation with a transaction.
We have to ask an intermediate guy (or agent), the FailureHandlingOptions, for help. The point is how to create an instance of the FailureHandlingOptions or where to find it?
Trying to create an instance from scratch would just fail as already shown in the above test code. The reason is simple, “The type 'Autodesk.Revit.DB.FailureHandlingOptions' has no constructors defined”. Fortunately, every Transaction has its own GetFailureHandlingOptions() method which can return an instance of the FailureHandlingOptions for us. After that, we can create an instance of our own ITransactionFinalizer by calling the method SetTransactionFinalizer().
Is it done yet?
No, not really. In the Commit or RollBack call of the Transaction of concern, the FailureHandlingOptions instance has to be fed back to the transaction. Sound weird and not necessary, right?
If every single Transaction keeps a single instance of FailureHandlingOptions class and the GetFailureHandlingOptions() method returns a reference to the instance (the normal behavior of .NET), why is it necessary to feed the same only single instance of the FailureHandlingOptions back to the same transaction?
If the GetFailureHandlingOptions() method does not really return the contained instance of the FailureHandlingOptions of the Transaction of concern but instead create a new instance, it seems necessary to let the Commit or Rollback recognize the new FailureHandlingOptions, but then the method name (GetFailureHandlingOptions) is apparently in question.
Or is a flag necessary to indicate whether the embedded FailureHandlingOptions should take effect or not?
Maybe or maybe not.
Anyway, the good news is that the test result is as expected, that is, the implemented methods of the ITransactionFinalizer are called at the right time in the expected places.
Transaction Finalizer of RevitAddinWizard can help implement the interface automatically in no time.
Recent Comments