How to Execute Dynamic Code C# in Revit API Using Roslyn .NET 8
In Revit, you often need to work with APIs that require re-compiling and deploying your application for each change. However, you might want to execute dynamically generated code at runtime without having to recompile your entire add-in. Using Roslyn, you can compile and run C# code dynamically at runtime.
This tutorial will walk you through creating a Revit external command that compiles and executes dynamic code at runtime. Many thanks Jeremy Tammik has have a awesome blog to sharing many idea about Revit API. This acticle also is a part of Revit Add-in Manager
1. Set Up Your Revit Add-in
Start by creating a new class in your Revit add-in project that implements IExternalCommand. This will allow us to execute our code within Revit.
using Autodesk.Revit.UI;
using Autodesk.Revit.DB;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using Autodesk.Revit.Attributes;
[Transaction(TransactionMode.Manual)]
public class ExecuteDynamicCodeCommand : IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
// The code to be executed dynamically as a string
string code = @"
using Autodesk.Revit.UI;
using Autodesk.Revit.DB;
using System.Linq;
using System.Collections.Generic;
public class DynamicWallCounter
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
Document doc = commandData.Application.ActiveUIDocument.Document;
// Get all walls
FilteredElementCollector collector = new FilteredElementCollector(doc);
collector.OfClass(typeof(Wall));
var walls = collector.ToElements().ToList();
// Display the wall count in the message
message = ""Total number of walls: "" + walls.Count.ToString();
TaskDialog.Show(""Wall Counter"", message);
return Result.Succeeded;
}
}";
2. Compile the Code Dynamically
We will use Roslyn to parse the dynamic C# code and compile it into a DLL in memory. First, we need to gather the required assemblies, including Revit API and some .NET runtime libraries.
// Compile the code using Roslyn
var syntaxTree = CSharpSyntaxTree.ParseText(code);
// Get currently loaded assemblies in Revit (including Revit API)
var references = AppDomain.CurrentDomain.GetAssemblies()
.Where(a => a.FullName.Contains("Autodesk.Revit"))
.Select(r => MetadataReference.CreateFromFile(r.Location))
.ToList();
// Add necessary .NET runtime assemblies
references.Add(MetadataReference.CreateFromFile(typeof(object).Assembly.Location)); // System.dll
references.Add(MetadataReference.CreateFromFile(typeof(System.Linq.Enumerable).Assembly.Location)); // System.Core.dll
references.Add(MetadataReference.CreateFromFile(typeof(System.Runtime.CompilerServices.TaskAwaiter).Assembly.Location)); // System.Runtime.dll
references.Add(MetadataReference.CreateFromFile(typeof(System.Runtime.ExceptionServices.ExceptionDispatchInfo).Assembly.Location)); // System.Runtime
references.Add(MetadataReference.CreateFromFile(typeof(System.Runtime.GCSettings).Assembly.Location)); // System.Runtime
3. Load Revit API Assemblies
You’ll need to explicitly reference the Revit API libraries (RevitAPI.dll and RevitAPIUI.dll) because Roslyn doesn’t automatically include them.
// Add Revit API assemblies explicitly
string revitApiPath = @"C:\Program Files\Autodesk\Revit 2025\RevitAPI.dll"; // Modify path if needed
string revitUiPath = @"C:\Program Files\Autodesk\Revit 2025\RevitAPIUI.dll"; // Modify path if needed
references.Add(MetadataReference.CreateFromFile(revitApiPath));
references.Add(MetadataReference.CreateFromFile(revitUiPath));
4. Create the Compilation and Emit the Assembly
After setting up references, we compile the code dynamically into an assembly stored in memory.
领英推è
// Create the compilation
var compilation = CSharpCompilation.Create("DynamicWallCounterAssembly")
.WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
.AddReferences(references)
.AddSyntaxTrees(syntaxTree);
// Emit the compiled assembly to a memory stream
using (var memoryStream = new MemoryStream())
{
var result = compilation.Emit(memoryStream);
if (!result.Success)
{
message = "Compilation failed:\n" + string.Join("\n", result.Diagnostics.Select(d => d.ToString()));
return Result.Failed;
}
5. Load the Assembly and Invoke the Method
Once the assembly is compiled, we load it into the application and invoke the method dynamically.
// Load the compiled assembly from the memory stream
memoryStream.Seek(0, SeekOrigin.Begin);
Assembly assembly = Assembly.Load(memoryStream.ToArray());
// Create an instance of the class
object instance = assembly.CreateInstance("DynamicWallCounter");
if (instance == null)
{
message = "Failed to create an instance of the class";
return Result.Failed;
}
// Invoke the Execute method dynamically
var executeMethod = instance.GetType().GetMethod("Execute");
if (executeMethod == null)
{
message = "Failed to find the 'Execute' method in the dynamic code.";
return Result.Failed;
}
try
{
executeMethod.Invoke(instance, new object[] { commandData, message, elements });
}
catch (Exception ex)
{
message = ex.ToString();
return Result.Failed;
}
}
return Result.Succeeded;
}
}
Let's see the output after execute code.
Conclusion
This idea has the potential to unlock numerous opportunities for time-saving in engineering by integrating AI to generate automated code and execute it within Revit. The key challenge lies in how we can apply this approach more efficiently to maximize its benefits.
Follow my medium to get all the special trick from software development in BIM : https://medium.com/@chuongmep
Resouces
Code example can see at : https://github.com/chuongmep/RevitAddInManager/blob/dev/Test/ExecuteDynamicCodeCommand.cs
- Ricaun also have similar concept here: https://github.com/ricaun-io/RevitAddin.CommandLoader
- bowerbird : Scripted C# Plug-in Framework
- Revit.ScriptCS : Run C# Scripts within the Revit Enviornment
- Fresh.Revit: F# Scripting editor for Revit
Infrastructure BIM Lead
3 天å‰Great work Chuong
Architecture ? Engineering ? Computational Design and Geometry
2 周I like how you break it down to the details. F# interactive scripting does the same. I packaged it up for Revit: https://github.com/goswinr/Fesh.Revit
Creating the Next Generation of Parametric 3D Design Tools
3 周Excellent tutorial Chuong Ho. It describes the technique I used to base my "Bowerbird" plugin on: https://github.com/ara3d/bowerbird.
Electrical Engineer | Revit API Developer | Arduino
3 周Nice! I did a plugin some time ago using Roslyn to compile IExternalCommand inside Revit, works fine to share sample commands. Here is the project: https://github.com/ricaun-io/RevitAddin.CommandLoader
AR-Powered BIM & CAD Viewer???? | Software Engineer
3 周Well done mate, I was struggling with this ??