Running Transaction from anywhere in the project

Running Transaction from anywhere in the project

harilalmnCBXDY
Enthusiast Enthusiast
410 Views
4 Replies
Message 1 of 5

Running Transaction from anywhere in the project

harilalmnCBXDY
Enthusiast
Enthusiast

Dear All,
This is to know if there are any drawbacks for this approach. While working with WPF windows, we usually use External Events and Handlers to run transactions. Here I have another approach and found working so far so good. 

I have a singleton class, called TransactionRunner, which is initialized within the IExternalCommand object. The current document is then set as the value of its 'Document' property. Here is the class;

public sealed class TransactionRunner
{
    private static TransactionRunner _instance;
    private static readonly object lockobject = new object();
    public Document Document { get; set; }
    public string TransactionName { get; set; } = "Transaction Runner";
    private TransactionRunner() { }
    public static TransactionRunner Instance
    {
        get
        {
            lock (lockobject) // thread safety
            {
                if (_instance == null)
                {
                    _instance = new TransactionRunner();
                }
                return _instance;
            }
        }
    }

    public void Execute(Action action, Action callback = null)
    {
        if (Document == null)
        {
            throw new System.Exception("Document is null");
        }

        using (Transaction t = new Transaction(Document, TransactionName))
        {
            t.Start();
            action();
            t.Commit();
        }

        callback?.Invoke();
    }
}

 

In my Command;

public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
    uidoc = commandData.Application.ActiveUIDocument;
    doc = uidoc.Document;

    TransactionRunner transactionRunner = TransactionRunner.Instance;
    transactionRunner.Document = doc;
    
    // Other calls....

    return Result.Succeeded;
}

Now I can call it like this from anywhere in the project;

TransactionRunner.TransactionName = "Some Transaction Name";

TransactionRunner.Instance.Execute(() => {
    // Make changes to the document
}, _callbackToUpdateSomething);

 

Does the approach have any potential issues...?

0 Likes
Accepted solutions (1)
411 Views
4 Replies
Replies (4)
Message 2 of 5

Kennan.Chen
Advocate
Advocate

The singleton instance you create will only function correctly when executed within the Revit API context, which is essentially equivalent to the main thread. While ExternalEvent is introduced as an asynchronous mechanism to ensure your code always executes within the Revit API context. If you're looking to execute transactions from any location, ExternalEvent is specifically designed for this purpose.

Message 3 of 5

harilalmnCBXDY
Enthusiast
Enthusiast

 @Kennan.Chen 

Thankyou for the quick response. I understood. I have a couple of queries;

 

What could be the scenario where in the transaction initiated could fail to execute?

 

If we have a window which runs multiple transactions at different times, say, run optionally based on certain user input on the window, do we have to create multiple IExternalEventHandlers and ExternalEvent instances and pass them to the window? Or is there a better way to do that?

0 Likes
Message 4 of 5

Kennan.Chen
Advocate
Advocate
Accepted solution

A typical scenario is when you want to run Revit API code in a modeless window. If you try to call the Revit API directly without using ExternalEvent, you'll encounter a Revit API exception.

 

One solution might be to use the third-party library I developed, Revit.Async. It essentially uses ExternalEvent, but you don't have to worry about those details. It allows you to execute Revit API code "anywhere".

 

You can find the repository here. And you can install it via nuget.

Message 5 of 5

harilalmnCBXDY
Enthusiast
Enthusiast

Will check it. Thankyou...!!

0 Likes