Message 1 of 3
Transaction cannot be started after async code (Revit.Async)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
I've created a minimal reproduceable example here. Can someone help me understand what's going on here, I'm not sure what I'm missing exactly.
I'm using Revit.Async because I need to call some async code.
- I create a task inside my command.
- I have an `AsyncGenericExternalEventHandler` named `TaskRunnerExternalEventHandler` whose express purpose is to be able to run tasks, and then execute a transaction based on the result of those tasks.
- The external event is registered in the `TestCommand` class constructor
In this configuration, when trying to start a transaction, it throws the classic "Starting a transaction from an external application running outside of API context is not allowed." However if I comment out `await Task.WhenAll(tasks).ConfigureAwait(true);` it suddenly works.
I tried to enforce `ConfigureAwait(true)` to be sure `SynchronizationContext` is the same. I checked the thread id both before and after the awaited task, and it remains `1`, so I'm sure I'm still on the main UI thread.
I'm not sure what I'm missing here...
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Revit.Async;
[Transaction(TransactionMode.Manual)]
public class TestCommand : IExternalCommand
{
public TestCommand()
{
RevitTask.RegisterGlobal(new TaskRunnerExternalEventHandler());
}
protected override Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
var task = Task.Delay(1000);
RevitTask.RaiseGlobal<TaskRunnerExternalEventHandler, IEnumerable<Task>, bool>([task]);
return Result.Succeeded;
}
}
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Revit.Async.ExternalEvents;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Forms;
public class TaskRunnerExternalEventHandler : AsyncGenericExternalEventHandler<IEnumerable<Task>, bool>
{
protected override async Task<bool> Handle(UIApplication app, IEnumerable<Task> tasks)
{
var threadId1 = Thread.CurrentThread.ManagedThreadId;
await Task.WhenAll(tasks).ConfigureAwait(true);
var threadId2 = Thread.CurrentThread.ManagedThreadId;
try
{
using (var transaction = new Transaction(app.ActiveUIDocument.Document, "async issue"))
{
transaction.Start();
// transaction is empty, symbolic only in this example
transaction.Commit();
}
}
catch (Exception e)
{
MessageBox.Show(e.Message);
return false;
}
return true;
}
public override string GetName()
{
return GetType().Name;
}
public override object Clone()
{
return this;
}
}