How to Use AssemblyLoadContext for Isolating Inventor Addin Dependencies
Solve version conflicts once and for all.

Autodesk Inventor previously allowed add-ins to use NuGet package/assembly versions that differ from the ones bundled with Inventor. However, with Autodesk Inventor 2025 and the framework upgrade to .NET 8, this no longer worked out of the box and caused version conflicts.
This article will demonstrate one way to resolve the version conflict by isolating the add-in’s dependencies in a separate container using AssemblyLoadContext.
A full sample application can be found on GitHub.
What is AssemblyLoadContext?
- It's the runtime's provider for locating and loading dependencies. Whenever a dependency is loaded, an AssemblyLoadContext instance is invoked to locate it.
- AssemblyLoadContext provides a service of locating, loading, and caching managed assemblies and other dependencies.
- To support dynamic code loading and unloading, it creates an isolated context for loading code and its dependencies in their own AssemblyLoadContext instance.
How to update the add-in?
Add IsolatedApplicationAddInServer.cs
This is used as the new entry point into the add-in. It is a wrapper around the ApplicationAddInServer. It adds additional functionality behind the scenes but still exposes the startup and shutdown methods called automatically by Inventor.
Activate(ApplicationAddInSite application, bool firstTime)=>OnActivate()Deactivate()=>OnDeactivate()
using Inventor;
namespace IsolatedInventorAddin;
/// <summary>
/// <see cref="ApplicationAddInServer" /> is the standard entry point of the Inventor addin.
/// This class is extended to have fully isolated addin dependency container.
/// Inherit this class and add custom logic to overrides of <see cref="OnActivate" /> or <see cref="OnDeactivate" />.
/// </summary>
public abstract class IsolatedApplicationAddInServer : ApplicationAddInServer
{
#if NETCOREAPP
private object? _isolatedInstance;
#endif
public object? Automation { get; set; } = null;
/// <summary>
/// Reference to the parameter in <see cref="ApplicationAddInServer.Activate" />.
/// </summary>
public ApplicationAddInSite ApplicationAddInSite { get; private set; } = default!;
/// <summary>
/// Reference to the parameter in <see cref="ApplicationAddInServer.Activate" />.
/// </summary>
public bool FirstTime { get; private set; } = default!;
public void Activate(ApplicationAddInSite applicationAddInSite, bool firstTime)
{
Type currentType = GetType();
#if NETCOREAPP
if (AddinLoadContext.CheckIfCustomContext(currentType) is false)
{
AddinLoadContext dependenciesProvider = AddinLoadContext.GetDependenciesProvider(currentType);
_isolatedInstance = dependenciesProvider.CreateAssemblyInstance(currentType);
AddinLoadContext.Invoke(_isolatedInstance, nameof(Activate), applicationAddInSite, firstTime);
return;
}
#endif
ApplicationAddInSite = applicationAddInSite;
FirstTime = firstTime;
#if NETCOREAPP
OnActivate();
#else
try
{
ResolveHelper.BeginAssemblyResolve(currentType);
OnActivate();
}
finally
{
ResolveHelper.EndAssemblyResolve();
}
#endif
}
public void Deactivate()
{
Type currentType = GetType();
#if NETCOREAPP
if (AddinLoadContext.CheckIfCustomContext(currentType) is false)
{
AddinLoadContext.Invoke(_isolatedInstance!, nameof(Deactivate));
return;
}
OnDeactivate();
#else
try
{
ResolveHelper.BeginAssemblyResolve(currentType);
OnDeactivate();
}
finally
{
ResolveHelper.EndAssemblyResolve();
}
#endif
}
[Obsolete("Deprecated in the Inventor API. Required for legacy compatibility.")]
public void ExecuteCommand(int CommandID) { }
/// <summary>
/// Overload this method to execute custom logic when the Inventor addin is loaded and <see cref="ApplicationAddInServer.Activate" /> method is executed.
/// </summary>
public abstract void OnActivate();
/// <summary>
/// Overload this method to execute custom logic when the Inventor addin is unloaded and <see cref="ApplicationAddInServer.Deactivate" /> method is executed.
/// </summary>
public abstract void OnDeactivate();
}
Add AddinLoadContext.cs
This is a custom implementation of the AssemblyLoadContext specifically for the format of an Inventor add-in.
#if NETCOREAPP
using Inventor;
using System.Reflection;
using System.Runtime.Loader;
namespace IsolatedInventorAddin;
/// <summary>
/// Isolated add-in dependency container.
/// </summary>
internal sealed class AddinLoadContext : AssemblyLoadContext
{
/// <summary>
/// Add-ins contexts storage.
/// </summary>
private static readonly Dictionary<string, AddinLoadContext> _dependenciesProviders = new(1);
private readonly AssemblyDependencyResolver _resolver;
private const BindingFlags MethodSearchFlags = BindingFlags.Public | BindingFlags.Instance;
private AddinLoadContext(Type type, string addinName) : base(addinName)
{
string addinLocation = type.Assembly.Location;
_resolver = new AssemblyDependencyResolver(addinLocation);
}
/// <summary>
/// Resolve dependency any time one is loaded if it exists in the isolated add-in dependency container.
/// </summary>
protected override Assembly? Load(AssemblyName assemblyName)
{
string? assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
return assemblyPath is not null ? LoadFromAssemblyPath(assemblyPath) : null;
}
/// <summary>
/// Determine whether the type is the type not associated with the default context.
/// </summary>
public static bool CheckAccess(Type type)
{
AssemblyLoadContext? currentContext = GetLoadContext(type.Assembly);
return currentContext != Default;
}
/// <summary>
/// Get or create a new isolated context for the type.
/// </summary>
public static AddinLoadContext GetDependenciesProvider(Type type)
{
// Assembly location used as context name and the unique provider key.
string addinRoot = System.IO.Path.GetDirectoryName(type.Assembly.Location)!;
if (_dependenciesProviders.TryGetValue(addinRoot, out var provider))
{
return provider;
}
string addinName = System.IO.Path.GetFileName(addinRoot);
provider = new AddinLoadContext(type, addinName);
_dependenciesProviders.Add(addinRoot, provider);
return provider;
}
/// <summary>
/// Create new instance in the separated context.
/// </summary>
public object CreateInstance(Type type)
{
string assemblyLocation = type.Assembly.Location;
Assembly assembly = LoadFromAssemblyPath(assemblyLocation);
return assembly.CreateInstance(type.FullName!)!;
}
/// <summary>
/// Execute <see cref="ApplicationAddInSite" /> in the separated context.
/// </summary>
/// <remarks>
/// Matches parameter format of <see cref="ApplicationAddInServer.Activate" /> method.
/// </remarks>
public static void Invoke(object instance, string methodName, ApplicationAddInSite application, bool firstTime)
{
Type instanceType = instance.GetType();
Type[] methodParameterTypes =
[
typeof(ApplicationAddInSite),
typeof(bool)
];
object[] methodParameters =
[
application,
firstTime
];
MethodInfo method = instanceType.GetMethod(methodName, MethodSearchFlags, null, methodParameterTypes, null)!;
// Call the inheriting method which may contain custom logic if it exists.
_ = method.Invoke(instance, methodParameters)!;
}
/// <remarks>
/// Matches parameter format of <see cref="ApplicationAddInServer.Deactivate" /> method.
/// </remarks>
public static void Invoke(object instance, string methodName)
{
Type instanceType = instance.GetType();
MethodInfo method = instanceType.GetMethod(methodName, MethodSearchFlags, null, [], null)!;
// Call the inheriting method which may contain custom logic if it exists.
_ = method.Invoke(instance, [])!;
}
}
#endif
Add ResolveHelper.cs
This is a helper class used to resolve conflicting versions of a dependency.
#if NETCOREAPP
using System.Runtime.Loader;
#endif
namespace IsolatedInventorAddin;
/// <summary>
/// Used to resolve conflicting versions of a dependency.
/// </summary>
public static class ResolveHelper
{
private static string? _moduleDirectory;
private static object? _domainResolvers;
/// <summary>
/// Subscribes the current domain to resolve dependencies for the type.
/// </summary>
/// <typeparam name="T">Type, to search for dependencies in the directory where this type is defined.</typeparam>
/// <remarks>
/// Dependencies are searched in a directory of the specified type.
/// At the time of dependency resolution, all other dependency resolution methods for the domain are disabled,
/// this requires calling <see cref="EndAssemblyResolve" /> immediately after executing user code where dependency failures occur.
/// </remarks>
public static void BeginAssemblyResolve<T>()
{
BeginAssemblyResolve(typeof(T));
}
/// <summary>
/// Subscribes the current domain to resolve dependencies for the type.
/// </summary>
/// <param name="type">Type, to search for dependencies in the directory where this type is defined.</param>
/// <remarks>
/// Dependencies are searched in a directory of the specified type.
/// At the time of dependency resolution, all other dependency resolution methods for the domain are disabled,
/// this requires calling <see cref="EndAssemblyResolve" /> immediately after executing user code where dependency failures occur.
/// </remarks>
public static void BeginAssemblyResolve(Type type)
{
if (_domainResolvers is not null)
return;
if (type.Module.FullyQualifiedName == "<Unknown>")
return;
#if NETCOREAPP
var loadContextType = typeof(AssemblyLoadContext);
var resolversField = loadContextType.GetField("AssemblyResolve", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly)!;
var resolvers = resolversField.GetValue(null);
resolversField.SetValue(null, null);
#else
var domainType = AppDomain.CurrentDomain.GetType();
var resolversField = domainType.GetField("_AssemblyResolve", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)!;
var resolvers = resolversField.GetValue(AppDomain.CurrentDomain);
resolversField.SetValue(AppDomain.CurrentDomain, null);
#endif
_domainResolvers = resolvers;
_moduleDirectory = System.IO.Path.GetDirectoryName(type.Module.FullyQualifiedName);
AppDomain.CurrentDomain.AssemblyResolve += OnAssemblyResolve;
}
/// <summary>
/// Unsubscribes the current domain to resolve dependencies for the type.
/// </summary>
public static void EndAssemblyResolve()
{
if (_domainResolvers is null)
return;
#if NETCOREAPP
var loadContextType = typeof(AssemblyLoadContext);
var resolversField = loadContextType.GetField("AssemblyResolve", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly)!;
resolversField.SetValue(null, _domainResolvers);
#else
var domainType = AppDomain.CurrentDomain.GetType();
var resolversField = domainType.GetField("_AssemblyResolve", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)!;
resolversField.SetValue(AppDomain.CurrentDomain, _domainResolvers);
#endif
_domainResolvers = null;
_moduleDirectory = null;
AppDomain.CurrentDomain.AssemblyResolve -= OnAssemblyResolve;
}
private static Assembly? OnAssemblyResolve(object? sender, ResolveEventArgs args)
{
string assemblyName = new AssemblyName(args.Name).Name;
var assemblyPath = System.IO.Path.Combine(_moduleDirectory!, $"{assemblyName}.dll");
if (System.IO.File.Exists(assemblyPath) is false)
return null;
return Assembly.LoadFrom(assemblyPath);
}
}
Update Project (.csproj) File
- Must set
PrivatetoFalseonAutodesk.Inventor.Interopassembly file reference. - Replace
2026with the version of Inventor being referenced.
<!-- Custom Variable -->
<PropertyGroup>
<InventorVersion>2026</InventorVersion>
</PropertyGroup>
<ItemGroup>
<Reference Include="Autodesk.Inventor.Interop">
<HintPath>$(ProgramFiles)\Autodesk\Inventor $(InventorVersion)\Bin\Public Assemblies\Autodesk.Inventor.Interop.dll</HintPath>
<!-- Required For Isolated Addin -->
<Private>False</Private>
</Reference>
</ItemGroup>
Notes
- This add-in format is compatible with Inventor versions using .NET Framework 4.8 or .NET 8.
- This add-in format is compatible and works as an
AppBundlewith the Automation API.
References
- Microsoft Learn - System.Runtime.Loader.AssemblyLoadContext
- GitHub - dotnet - AssemblyLoadContext.md



