The Notorious... "Starting a transaction from an external application running outside of API context is not allowed."
Tags: revit, api, beginners, devjournal
Published: 2023-01-23 17:44:47

Image by <a href="https://pixabay.com/users/irina_kukuts-1213707/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=2762156">Irina</a> from <a href="https://pixabay.com//?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=2762156">Pixabay</a>
##Table of Contents
   * [The Exception](#exception)
   * [IExternalEventHandler](#iexternal-event-handler)
   * [IExternalCommand](#iexternal-command)
   * [Event.Raise();](#event-raise)
   * [Final Thoughts](#final)
   * [Resources](#resources)

##The Exception <a name="exception"></a>
Chances are if you have tried to create a modeless dialog in Revit without the proper knowledge of [ExternalEvent Class](https://www.revitapidocs.com/2022/05089477-4612-35b2-81a2-89c4f44370ea.htm) and the [IExternalEventHandler Interface](https://www.revitapidocs.com/2022/f265a2c9-8540-9c97-9b37-4f7442becac2.htm) you may have run into this error, "Starting a transaction from an external application running outside of API context is not allowed." Now, this message is pretty self explanatory but sometimes in the heat of the moment messages such as these can feel extremely confusing. When creating a modeless WPF window or form we are technically not even in the Revit context and in order to "jump back in" so to speak we have raise an event. I have found that so far anything Transaction based must be done within the Revit API context. In order to preform two of the basic CRUD operations (Create and Update) with Revit's DB we must open a transaction. For my scenario I have been trying to create new family instances in a Revit model(I am using Revit 2022 btw). I first ran into this error when I tried opening and committing a transaction in my xaml.cs code behind. Within this code I was not raising an external event to let Revit know I am trying to operate back in the API context.

So I create a class that implements the 
##IExternalEventHandler interface <a name="iexternal-event-handler"></a>

```javascript
public class MyEventHandler : IExternalEventHandler
{
    public static ExternalEvent HandlerEvent = null;
    public static MyEventHandler Handler = null;
    public void Execute(UIApplication app)
    {
        Document doc = app.ActiveUIDocument.Document;
        try
        {
            //notifying me of raised event
            Trace.WriteLine("Raised");
            try
            {
                using (Transaction tx = new Transaction(doc, "name"))
                {
                    tx.Start();
//within this transaction and event handler execute code block 
//is where the interaction with Revit is done.
                    tx.Commit();
                }
             }
             catch (e)
             {
                //catch whatever exception
                throw e;
             }
        }
        catch (InvalidOperationException)
        {

            throw;
        }
     }

        public string GetName()
        {
            return "My Event Handler";
        }
        public void GetData(MechEq mechEq)
        {
            data = mechEq;
        }
}
```
And then in my other class that inherits the IExternalCommand interface, the event and the handler is passed into the MainWindow class of my wpf.

##IExternalCommand interface <a name="iexternal-command"></a>
```javascript
public class MyClass : IExternalCommand
{
    public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
    {
        UIDocument uidoc = commandData.Application.ActiveUIDocument;
        Document doc = uidoc.Document;
        try
        {
            MyEventHandler handler = new MyEventHandler();
            ExternalEvent exEvent = ExternalEvent.Create(handler);
            MainWindow window = new MainWindow(uidoc, exEvent, handler);

            window.Show();
                
            return Result.Succeeded;

        }
        catch (Exception e)
        {

            throw e;
        }
    }
}
```
And then lastly in my MainWindow, in one of my button events I have my external event being raised.
#Event.Raise(); <a name="event-raise"></a>
```javascript
public MainWindow(UIDocument UiDoc, ExternalEvent exEvent, MyEventHandler handler)
{
    uidoc = UiDoc;
    doc = UiDoc.Document;
    InitializeComponent();
    m_exEvent = exEvent;
    myEvent = handler;
}

private void btnImport_Click(object sender, RoutedEventArgs e)
{
//raise event to trigger the execute method in MyEventHandler
    m_exEvent.Raise();
}
```
##Final thoughts <a name="final"></a>
This is a topic that has been covered in much detail by the Revit community. Jeremy Tammik has several examples covered in such detail on his website [the building coder](https://thebuildingcoder.typepad.com/) and you can search modeless dialogs in the search bar. The way I found some examples was simply by googling how to make modeless windows in Revit. There are tons of resources and a lot can be found just by googling, searching other peoples code, and the Autodesk Revit API forums. The funny thing is I was trying to understand this before I got the error and was having trouble grasping the concept. My plugin was working just fine but I was not making any transactions yet. Finally once I got to the point in making a transaction, I received the error and it made much more sense on what needed to be done once I received the exception. Anyways, keep truckin and cheers!

##Resources <a name="resources"></a> 
Whatever you do, do not create another forum post on the revit api forum with out doing some thorough research on this subject first!
   * [Revit API forum](https://forums.autodesk.com/t5/revit-api-forum/bd-p/160)
   * [Autodesk Developer Network](https://www.autodesk.com/developer-network/platform-technologies/revit)
   * [Revit SDK Samples](https://github.com/jeremytammik/RevitSdkSamples)