# How to Use AssemblyLoadContext for Isolating Vault Addin Dependencies

Autodesk Vault previously allowed add-ins to use NuGet package/assembly versions that differ from the ones bundled with Vault. However, with Autodesk Vault 2026 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](https://github.com/tylerwarner33/autodesk-vault-assembly-load-context) can be found on GitHub.


## What is [AssemblyLoadContext](https://learn.microsoft.com/en-us/dotnet/core/dependency-loading/understanding-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 `IsolatedIExplorerExtension.cs`

This is used as the new entry point into the add-in. It is a wrapper around the `IExplorerExtension`. It adds additional functionality behind the scenes but still exposes all methods called automatically by Vault.
- `OnStartup(IApplication application)` => `Startup()`
- `OnLogOn(IApplication application)` => `LogOn()`
- `OnLogOff(IApplication application)` => `LogOff()`
- `OnShutdown(IApplication application)` => `Shutdown()`
- `CommandSites()` => `OnCommandSites()`
- `CustomEntityHandlers()` => `OnCustomEntityHandlers()`
- `DetailTabs()` => `OnDetailTabs()`
- `DockPanels()` => `OnDockPanels()` (For Vault 2026+)
- `HiddenCommands()` => `OnHiddenCommands()`

```csharp
﻿using Autodesk.Connectivity.Explorer.Extensibility;
using System.ComponentModel;

namespace IsolatedVaultAddin.Isolation;

/// <summary>
///	<see cref="IExplorerExtension" /> is the entry point of the Vault addin.
///	This class is extended to have fully isolated addin dependency container.
///	Inherit this class and add custom logic to overrides of <see cref="Startup" />, <see cref="LogOn" />, <see cref="LogOff" />, <see cref="Shutdown" />, <see cref="OnCommandSites" />, <see cref="OnCustomEntityHandlers" />, <see cref="OnDetailTabs" />, <see cref="OnDockPanels" />, or <see cref="OnHiddenCommands" />.
/// </summary>
public abstract class IsolatedIExplorerExtension : IExplorerExtension
{
#if NETCOREAPP
	private object? _isolatedInstance;
#endif

	/// <summary>
	///	Reference to the parameter in <see cref="IExplorerExtension.OnStartup" />, <see cref="IExplorerExtension.OnLogOn" />, <see cref="IExplorerExtension.OnLogOff" />, <see cref="IExplorerExtension.OnShutdown" />.
	/// </summary>
	public IApplication Application { get; private set; } = default!;

	[EditorBrowsable(EditorBrowsableState.Never)]
	public void OnStartup(IApplication application)
	{
		Type currentType = GetType();

#if NETCOREAPP
		if (AddinLoadContext.CheckIfCustomContext(currentType) is false)
		{
			AddinLoadContext dependenciesProvider = AddinLoadContext.GetDependenciesProvider(currentType);
			_isolatedInstance = dependenciesProvider.CreateAssemblyInstance(currentType);

			AddinLoadContext.Invoke(_isolatedInstance, nameof(OnStartup), application);
			return;
		}
#endif

		Application = application;

#if NETCOREAPP
		Startup();
#else
		try
		{
			ResolveHelper.BeginAssemblyResolve(currentType);
			Startup();
		}
		finally
		{
			ResolveHelper.EndAssemblyResolve();
		}
#endif
	}

	[EditorBrowsable(EditorBrowsableState.Never)]
	public void OnLogOn(IApplication application)
	{
		Type currentType = GetType();

#if NETCOREAPP
		if (AddinLoadContext.CheckIfCustomContext(currentType) is false)
		{
			AddinLoadContext dependenciesProvider = AddinLoadContext.GetDependenciesProvider(currentType);
			_isolatedInstance = dependenciesProvider.CreateAssemblyInstance(currentType);

			AddinLoadContext.Invoke(_isolatedInstance, nameof(OnLogOn), application);
			return;
		}
#endif

		Application = application;

#if NETCOREAPP
		LogOn();
#else
		try
		{
			ResolveHelper.BeginAssemblyResolve(currentType);
			LogOn();
		}
		finally
		{
			ResolveHelper.EndAssemblyResolve();
		}
#endif
	}

	[EditorBrowsable(EditorBrowsableState.Never)]
	public void OnLogOff(IApplication application)
	{
		Type currentType = GetType();

#if NETCOREAPP
		if (AddinLoadContext.CheckIfCustomContext(currentType) is false)
		{
			AddinLoadContext dependenciesProvider = AddinLoadContext.GetDependenciesProvider(currentType);
			_isolatedInstance = dependenciesProvider.CreateAssemblyInstance(currentType);

			AddinLoadContext.Invoke(_isolatedInstance, nameof(OnLogOff), application);
			return;
		}
#endif

		Application = application;

#if NETCOREAPP
		LogOff();
#else
		try
		{
			ResolveHelper.BeginAssemblyResolve(currentType);
			LogOff();
		}
		finally
		{
			ResolveHelper.EndAssemblyResolve();
		}
#endif
	}

	[EditorBrowsable(EditorBrowsableState.Never)]
	public void OnShutdown(IApplication application)
	{
		Type currentType = GetType();

#if NETCOREAPP
		if (AddinLoadContext.CheckIfCustomContext(currentType) is false)
		{
			AddinLoadContext dependenciesProvider = AddinLoadContext.GetDependenciesProvider(currentType);
			_isolatedInstance = dependenciesProvider.CreateAssemblyInstance(currentType);

			AddinLoadContext.Invoke(_isolatedInstance, nameof(OnShutdown), application);
			return;
		}
#endif

		Application = application;

#if NETCOREAPP
		Shutdown();
#else
		try
		{
			ResolveHelper.BeginAssemblyResolve(currentType);
			Shutdown();
		}
		finally
		{
			ResolveHelper.EndAssemblyResolve();
		}
#endif
	}

	[EditorBrowsable(EditorBrowsableState.Never)]
	public IEnumerable<CommandSite>? CommandSites()
	{
		Type currentType = GetType();

#if NETCOREAPP
		if (AddinLoadContext.CheckIfCustomContext(currentType) is false)
		{
			AddinLoadContext dependenciesProvider = AddinLoadContext.GetDependenciesProvider(currentType);
			_isolatedInstance = dependenciesProvider.CreateAssemblyInstance(currentType);

			return AddinLoadContext.Invoke<CommandSite>(_isolatedInstance, nameof(CommandSites));
		}
#endif

#if NETCOREAPP
		return OnCommandSites();
#else
		try
		{
			ResolveHelper.BeginAssemblyResolve(currentType);
			return OnCommandSites();
		}
		finally
		{
			ResolveHelper.EndAssemblyResolve();
		}
#endif
	}

	[EditorBrowsable(EditorBrowsableState.Never)]
	public IEnumerable<CustomEntityHandler>? CustomEntityHandlers()
	{
		Type currentType = GetType();

#if NETCOREAPP
		if (AddinLoadContext.CheckIfCustomContext(currentType) is false)
		{
			AddinLoadContext dependenciesProvider = AddinLoadContext.GetDependenciesProvider(currentType);
			_isolatedInstance = dependenciesProvider.CreateAssemblyInstance(currentType);

			return AddinLoadContext.Invoke<CustomEntityHandler>(_isolatedInstance, nameof(CustomEntityHandlers));
		}
#endif

#if NETCOREAPP
		return OnCustomEntityHandlers();
#else
		try
		{
			ResolveHelper.BeginAssemblyResolve(currentType);
			return OnCustomEntityHandlers();
		}
		finally
		{
			ResolveHelper.EndAssemblyResolve();
		}
#endif
	}

	[EditorBrowsable(EditorBrowsableState.Never)]
	public IEnumerable<DetailPaneTab>? DetailTabs()
	{
		Type currentType = GetType();

#if NETCOREAPP
		if (AddinLoadContext.CheckIfCustomContext(currentType) is false)
		{
			AddinLoadContext dependenciesProvider = AddinLoadContext.GetDependenciesProvider(currentType);
			_isolatedInstance = dependenciesProvider.CreateAssemblyInstance(currentType);

			return AddinLoadContext.Invoke<DetailPaneTab>(_isolatedInstance, nameof(DetailTabs));
		}
#endif

#if NETCOREAPP
		return OnDetailTabs();
#else
		try
		{
			ResolveHelper.BeginAssemblyResolve(currentType);
			return OnDetailTabs();
		}
		finally
		{
			ResolveHelper.EndAssemblyResolve();
		}
#endif
	}

#if VAULT_HAS_DOCK_PANELS
	[EditorBrowsable(EditorBrowsableState.Never)]
	public IEnumerable<DockPanel>? DockPanels()
	{
		Type currentType = GetType();

#if NETCOREAPP
		if (AddinLoadContext.CheckIfCustomContext(currentType) is false)
		{
			AddinLoadContext dependenciesProvider = AddinLoadContext.GetDependenciesProvider(currentType);
			_isolatedInstance = dependenciesProvider.CreateAssemblyInstance(currentType);

			return AddinLoadContext.Invoke<DockPanel>(_isolatedInstance, nameof(DockPanels));
		}
#endif

#if NETCOREAPP
		return OnDockPanels();
#else
		try
		{
			ResolveHelper.BeginAssemblyResolve(currentType);
			return OnDockPanels();
		}
		finally
		{
			ResolveHelper.EndAssemblyResolve();
		}
#endif
	}
#endif

	[EditorBrowsable(EditorBrowsableState.Never)]
	public IEnumerable<string>? HiddenCommands()
	{
		Type currentType = GetType();

#if NETCOREAPP
		if (AddinLoadContext.CheckIfCustomContext(currentType) is false)
		{
			AddinLoadContext dependenciesProvider = AddinLoadContext.GetDependenciesProvider(currentType);
			_isolatedInstance = dependenciesProvider.CreateAssemblyInstance(currentType);

			return AddinLoadContext.Invoke<string>(_isolatedInstance, nameof(HiddenCommands));
		}
#endif

#if NETCOREAPP
		return OnHiddenCommands();
#else
		try
		{
			ResolveHelper.BeginAssemblyResolve(currentType);
			return OnHiddenCommands();
		}
		finally
		{
			ResolveHelper.EndAssemblyResolve();
		}
#endif
	}

	/// <summary>
	///	Overload this method to execute custom logic when the Vault addin is loaded and <see cref="IExplorerExtension.OnStartup" /> method is executed.
	/// </summary>
	public abstract void Startup();

	/// <summary>
	///	Overload this method to execute custom logic when the Vault is logged into and <see cref="IExplorerExtension.OnLogOn" /> method is executed.
	/// </summary>
	public abstract void LogOn();

	/// <summary>
	///	Overload this method to execute custom logic when the Vault is logged out of and <see cref="IExplorerExtension.OnLogOff" /> method is executed.
	/// </summary>
	public abstract void LogOff();

	/// <summary>
	///	Overload this method to execute custom logic when the Vault addin is unloaded and <see cref="IExplorerExtension.OnShutdown" /> method is executed.
	/// </summary>
	public abstract void Shutdown();

	/// <summary>
	///	Overload this method to execute custom logic when the Vault addin ... and <see cref="IExplorerExtension.CommandSites" /> method is executed.
	/// </summary>
	public abstract IEnumerable<CommandSite>? OnCommandSites();

	/// <summary>
	///	Overload this method to execute custom logic when the Vault addin ... and <see cref="IExplorerExtension.CustomEntityHandlers" /> method is executed.
	/// </summary>
	public abstract IEnumerable<CustomEntityHandler>? OnCustomEntityHandlers();

	/// <summary>
	///	Overload this method to execute custom logic when the Vault addin ... and <see cref="IExplorerExtension.DetailTabs" /> method is executed.
	/// </summary>
	public abstract IEnumerable<DetailPaneTab>? OnDetailTabs();

#if VAULT_HAS_DOCK_PANELS
	/// <summary>
	///	Overload this method to execute custom logic when the Vault addin ... and <see cref="IExplorerExtension.DockPanels" /> method is executed.
	/// </summary>
	public abstract IEnumerable<DockPanel>? OnDockPanels();
#endif	

	/// <summary>
	///	Overload this method to execute custom logic when the Vault addin ... and <see cref="IExplorerExtension.HiddenCommands" /> method is executed.
	/// </summary>
	public abstract IEnumerable<string>? OnHiddenCommands();
}
```

### Add `IsolatedIWebServiceExtension .cs`

This is used as the new entry point into the add-in. It is a wrapper around the `IWebServiceExtension`. It adds additional functionality behind the scenes but still exposes all methods called automatically by Vault.
- `OnLoad()` => `Load()`

```csharp
﻿﻿using Autodesk.Connectivity.WebServices;
using System.ComponentModel;

namespace IsolatedVaultAddin.Isolation;

/// <summary>
///	<see cref="IWebServiceExtension" /> is the entry point of the Vault addin.
///	This class is extended to have fully isolated addin dependency container.
///	Inherit this class and add custom logic to overrides of <see cref="Load" />.
/// </summary>
public abstract class IsolatedIWebServiceExtension : IWebServiceExtension
{
#if NETCOREAPP
	private object? _isolatedInstance;
#endif

	[EditorBrowsable(EditorBrowsableState.Never)]
	public void OnLoad()
	{
		Type currentType = GetType();

#if NETCOREAPP
		if (AddinLoadContext.CheckIfCustomContext(currentType) is false)
		{
			AddinLoadContext dependenciesProvider = AddinLoadContext.GetDependenciesProvider(currentType);
			_isolatedInstance = dependenciesProvider.CreateAssemblyInstance(currentType);

			AddinLoadContext.Invoke(_isolatedInstance, nameof(OnLoad));
			return;
		}
#endif

#if NETCOREAPP
		Load();
#else
		try
		{
			ResolveHelper.BeginAssemblyResolve(currentType);
			Load();
		}
		finally
		{
			ResolveHelper.EndAssemblyResolve();
		}
#endif
	}

	/// <summary>
	///	Overload this method to execute custom logic when the Vault addin is loaded and <see cref="IWebServiceExtension.OnLoad" /> method is executed.
	/// </summary>
	public abstract void Load();
}
```

### Add `IsolatedIJobHandler.cs`

This is used as the new entry point into the add-in. It is a wrapper around the `IJobHandler`. It adds additional functionality behind the scenes but still exposes all methods called automatically by Vault.
- `Execute(IJobProcessorServices context, IJob job)` => `OnExecute()`
- `CanProcess(string jobType)` => `OnCanProcess()`
- `OnJobProcessorStartup(IJobProcessorServices context)` => `JobProcessorStartup()`
- `OnJobProcessorShutdown(IJobProcessorServices context)` => `JobProcessorShutdown()`
- `OnJobProcessorWake(IJobProcessorServices context)` => `JobProcessorWake()`
- `OnJobProcessorSleep(IJobProcessorServices context)` => `JobProcessorSleep()`

```csharp
﻿﻿using Autodesk.Connectivity.Explorer.Extensibility;
using Autodesk.Connectivity.JobProcessor.Extensibility;
using System.ComponentModel;

namespace IsolatedVaultAddin.Isolation;

/// <summary>
///	<see cref="IJobHandler" /> is the entry point of the Vault job processor addin.
///	This class is extended to have fully isolated addin dependency container.
///	Inherit this class and add custom logic to overrides of <see cref="OnExecute" />, <see cref="OnCanProcess" />, <see cref="JobProcessorStartup" />, <see cref="JobProcessorShutdown" />, <see cref="JobProcessorWake" />, or <see cref="JobProcessorSleep" />.
/// </summary>
public abstract class IsolatedIJobHandler : IJobHandler
{
#if NETCOREAPP
	private object? _isolatedInstance;
#endif

	/// <summary>
	///	Reference to the parameter in <see cref="IExplorerExtension.Execute" />, <see cref="IExplorerExtension.OnJobProcessorStartup" />, <see cref="IExplorerExtension.OnJobProcessorShutdown" />, <see cref="IExplorerExtension.OnJobProcessorWake" />, <see cref="IExplorerExtension.OnJobProcessorSleep" />.
	/// </summary>
	public IJobProcessorServices Context { get; private set; } = default!;

	/// <summary>
	///	Reference to the parameter in <see cref="IExplorerExtension.Execute" />.
	/// </summary>
	public IJob Job { get; private set; } = default!;

	/// <summary>
	///	Reference to the parameter in <see cref="IExplorerExtension.CanProcess" />.
	/// </summary>
	public string JobType { get; private set; } = default!;

	[EditorBrowsable(EditorBrowsableState.Never)]

	public JobOutcome Execute(IJobProcessorServices context, IJob job)
	{
		Type currentType = GetType();

#if NETCOREAPP
		if (AddinLoadContext.CheckIfCustomContext(currentType) is false)
		{
			AddinLoadContext dependenciesProvider = AddinLoadContext.GetDependenciesProvider(currentType);
			_isolatedInstance = dependenciesProvider.CreateAssemblyInstance(currentType);

			return AddinLoadContext.Invoke(_isolatedInstance, nameof(Execute), context, job);
		}
#endif

		Context = context;
		Job = job;

#if NETCOREAPP
		return OnExecute();
#else
		try
		{
			ResolveHelper.BeginAssemblyResolve(currentType);
			return OnExecute();
		}
		finally
		{
			ResolveHelper.EndAssemblyResolve();
		}
#endif
	}

	[EditorBrowsable(EditorBrowsableState.Never)]
	public bool CanProcess(string jobType)
	{
		Type currentType = GetType();

#if NETCOREAPP
		if (AddinLoadContext.CheckIfCustomContext(currentType) is false)
		{
			AddinLoadContext dependenciesProvider = AddinLoadContext.GetDependenciesProvider(currentType);
			_isolatedInstance = dependenciesProvider.CreateAssemblyInstance(currentType);

			return AddinLoadContext.Invoke(_isolatedInstance, nameof(CanProcess), jobType);
		}
#endif

		JobType = jobType;

#if NETCOREAPP
		return OnCanProcess();
#else
		try
		{
			ResolveHelper.BeginAssemblyResolve(currentType);
			return OnCanProcess();
		}
		finally
		{
			ResolveHelper.EndAssemblyResolve();
		}
#endif
	}

	[EditorBrowsable(EditorBrowsableState.Never)]
	public void OnJobProcessorStartup(IJobProcessorServices context)
	{
		Type currentType = GetType();

#if NETCOREAPP
		if (AddinLoadContext.CheckIfCustomContext(currentType) is false)
		{
			AddinLoadContext dependenciesProvider = AddinLoadContext.GetDependenciesProvider(currentType);
			_isolatedInstance = dependenciesProvider.CreateAssemblyInstance(currentType);

			AddinLoadContext.Invoke(_isolatedInstance, nameof(OnJobProcessorStartup), context);
			return;
		}
#endif

		Context = context;

#if NETCOREAPP
		JobProcessorStartup();
#else
		try
		{
			ResolveHelper.BeginAssemblyResolve(currentType);
			JobProcessorStartup();
		}
		finally
		{
			ResolveHelper.EndAssemblyResolve();
		}
#endif
	}

	[EditorBrowsable(EditorBrowsableState.Never)]
	public void OnJobProcessorShutdown(IJobProcessorServices context)
	{
		Type currentType = GetType();

#if NETCOREAPP
		if (AddinLoadContext.CheckIfCustomContext(currentType) is false)
		{
			AddinLoadContext dependenciesProvider = AddinLoadContext.GetDependenciesProvider(currentType);
			_isolatedInstance = dependenciesProvider.CreateAssemblyInstance(currentType);

			AddinLoadContext.Invoke(_isolatedInstance, nameof(OnJobProcessorShutdown), context);
			return;
		}
#endif

		Context = context;

#if NETCOREAPP
		JobProcessorShutdown();
#else
		try
		{
			ResolveHelper.BeginAssemblyResolve(currentType);
			JobProcessorShutdown();
		}
		finally
		{
			ResolveHelper.EndAssemblyResolve();
		}
#endif
	}

	[EditorBrowsable(EditorBrowsableState.Never)]
	public void OnJobProcessorWake(IJobProcessorServices context)
	{
		Type currentType = GetType();

#if NETCOREAPP
		if (AddinLoadContext.CheckIfCustomContext(currentType) is false)
		{
			AddinLoadContext dependenciesProvider = AddinLoadContext.GetDependenciesProvider(currentType);
			_isolatedInstance = dependenciesProvider.CreateAssemblyInstance(currentType);

			AddinLoadContext.Invoke(_isolatedInstance, nameof(OnJobProcessorWake), context);
			return;
		}
#endif

		Context = context;

#if NETCOREAPP
		JobProcessorWake();
#else
		try
		{
			ResolveHelper.BeginAssemblyResolve(currentType);
			JobProcessorWake();
		}
		finally
		{
			ResolveHelper.EndAssemblyResolve();
		}
#endif
	}

	[EditorBrowsable(EditorBrowsableState.Never)]
	public void OnJobProcessorSleep(IJobProcessorServices context)
	{
		Type currentType = GetType();

#if NETCOREAPP
		if (AddinLoadContext.CheckIfCustomContext(currentType) is false)
		{
			AddinLoadContext dependenciesProvider = AddinLoadContext.GetDependenciesProvider(currentType);
			_isolatedInstance = dependenciesProvider.CreateAssemblyInstance(currentType);

			AddinLoadContext.Invoke(_isolatedInstance, nameof(OnJobProcessorSleep), context);
			return;
		}
#endif

		Context = context;

#if NETCOREAPP
		JobProcessorSleep();
#else
		try
		{
			ResolveHelper.BeginAssemblyResolve(currentType);
			JobProcessorSleep();
		}
		finally
		{
			ResolveHelper.EndAssemblyResolve();
		}
#endif
	}

	/// <summary>
	///	Overload this method to execute custom logic when the Vault job processor is ... and <see cref="IJobHandler.ExecuteAsync" /> method is executed.
	/// </summary>
	public abstract JobOutcome OnExecute();

	/// <summary>
	///	Overload this method to execute custom logic when the Vault job processor is ... and <see cref="IJobHandler.CanProcess" /> method is executed.
	/// </summary>
	public abstract bool OnCanProcess();

	/// <summary>
	///	Overload this method to execute custom logic when the Vault job processor is ... and <see cref="IJobHandler.OnJobProcessorStartup" /> method is executed.
	/// </summary>
	public abstract void JobProcessorStartup();

	/// <summary>
	///	Overload this method to execute custom logic when the Vault job processor is ... and <see cref="IJobHandler.OnJobProcessorShutdown" /> method is executed.
	/// </summary>
	public abstract void JobProcessorShutdown();

	/// <summary>
	///	Overload this method to execute custom logic when the Vault job processor is ... and <see cref="IJobHandler.OnJobProcessorWake" /> method is executed.
	/// </summary>
	public abstract void JobProcessorWake();

	/// <summary>
	///	Overload this method to execute custom logic when the Vault job processor is ... and <see cref="IJobHandler.OnJobProcessorSleep" /> method is executed.
	/// </summary>
	public abstract void JobProcessorSleep();
}
```


### Add `AddinLoadContext.cs`

This is a custom implementation of the `AssemblyLoadContext` specifically for the format of a Vault add-in.

```csharp
﻿#if NETCOREAPP
using Autodesk.Connectivity.Explorer.Extensibility;
using Autodesk.Connectivity.JobProcessor.Extensibility;
using Autodesk.Connectivity.WebServices;
using System.Reflection;
using System.Runtime.Loader;

namespace IsolatedVaultAddin.Isolation;

/// <summary>
///	Isolated addin dependency container.
/// </summary>
/// <remarks>
///	References:
///	<a href="https://learn.microsoft.com/en-us/dotnet/core/tutorials/creating-app-with-plugin-support">
///	Microsoft/Tutorials/Plugins
///	</a>
///	<a href="https://github.com/dotnet/coreclr/blob/v2.1.0/Documentation/design-docs/assemblyloadcontext.md">
///	GitHub/DotNet/CoreCLR
///	</a>
/// </remarks>
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 and load dependency any time one is loaded if it exists in the isolated addin dependency container.
	/// </summary>
	protected override Assembly? Load(AssemblyName assemblyName)
	{
		string? assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);

		return assemblyPath is not null
			 ? LoadFromAssemblyPath(assemblyPath)
			 : null;
	}

	/// <summary>
	///   Resolve and load unmanaged native dependency any time one is loaded if it exists in the isolated addin dependency container.
	/// </summary>
	protected override nint LoadUnmanagedDll(string assemblyName)
	{
		string? assemblyPath = _resolver.ResolveUnmanagedDllToPath(assemblyName);

		return assemblyPath is not null
			 ? LoadUnmanagedDllFromPath(assemblyPath)
			 : nint.Zero;
	}

	/// <summary>
	///   Determine if the <see cref="AssemblyLoadContext" /> is custom or still the default context.
	/// </summary>
	public static bool CheckIfCustomContext(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 CreateAssemblyInstance(Type type)
	{
		string assemblyLocation = type.Assembly.Location;
		Assembly assembly = LoadFromAssemblyPath(assemblyLocation);

		return assembly.CreateInstance(type.FullName!)!;
	}

	/// <summary>
	///   Execute <see cref="IExplorerExtension.OnStartup" />, <see cref="IExplorerExtension.OnLogOn" />, <see cref="IExplorerExtension.OnLogOff" />, <see cref="IExplorerExtension.OnShutdown" /> methods in the isolated context.
	/// </summary>
	/// <remarks>
	///   Matches parameter format of <see cref="IExplorerExtension.OnStartup" />, <see cref="IExplorerExtension.OnLogOn" />, <see cref="IExplorerExtension.OnLogOff" />, <see cref="IExplorerExtension.OnShutdown" /> methods.
	/// </remarks>
	public static void Invoke(object instance, string methodName, IApplication application)
	{
		Type instanceType = instance.GetType();

		Type[] methodParameterTypes =
		[
			 typeof(IApplication)
		];

		object[] methodParameters =
		[
			 application
		];

		MethodInfo method = instanceType.GetMethod(methodName, MethodSearchFlags, null, methodParameterTypes, null)!;

		_ = method.Invoke(instance, methodParameters)!;
	}

	/// <summary>
	///   Execute <see cref="IJobHandler.OnJobProcessorStartup" />, <see cref="IJobHandler.OnJobProcessorShutdown" />, <see cref="IJobHandler.OnJobProcessorWake" />, <see cref="IJobHandler.OnJobProcessorSleep" /> methods in the isolated context.
	/// </summary>
	/// <remarks>
	///   Matches parameter format of <see cref="IJobHandler.OnJobProcessorStartup" />, <see cref="IJobHandler.OnJobProcessorShutdown" />, <see cref="IJobHandler.OnJobProcessorWake" />, <see cref="IJobHandler.OnJobProcessorSleep" /> methods.
	/// </remarks>
	public static void Invoke(object instance, string methodName, IJobProcessorServices context)
	{
		Type instanceType = instance.GetType();

		Type[] methodParameterTypes =
		[
			 typeof(IJobProcessorServices)
		];

		object[] methodParameters =
		[
			 context!
		];

		MethodInfo method = instanceType.GetMethod(methodName, MethodSearchFlags, null, methodParameterTypes, null)!;

		_ = method.Invoke(instance, methodParameters)!;
	}

	/// <summary>
	///   Execute <see cref="IJobHandler.Execute" /> method in the isolated context.
	/// </summary>
	/// <remarks>
	///   Matches parameter format of <see cref="IJobHandler.Execute" /> method.
	/// </remarks>
	public static JobOutcome Invoke(object instance, string methodName, IJobProcessorServices context, IJob job)
	{
		Type instanceType = instance.GetType();

		Type[] methodParameterTypes =
		[
			 typeof(IJobProcessorServices),
			 typeof(IJob)
		];

		object[] methodParameters =
		[
			 context!,
			 job!
		];

		MethodInfo method = instanceType.GetMethod(methodName, MethodSearchFlags, null, methodParameterTypes, null)!;

		return (JobOutcome)method.Invoke(instance, methodParameters)!;
	}

	/// <summary>
	///   Execute <see cref="IJobHandler.CanProcess" /> method in the isolated context.
	/// </summary>
	/// <remarks>
	///   Matches parameter format of <see cref="IJobHandler.CanProcess" /> method.
	/// </remarks>
	public static bool Invoke(object instance, string methodName, string jobType)
	{
		Type instanceType = instance.GetType();

		Type[] methodParameterTypes =
		[
			 typeof(string)
		];

		object[] methodParameters =
		[
			 jobType
		];

		MethodInfo method = instanceType.GetMethod(methodName, MethodSearchFlags, null, methodParameterTypes, null)!;

		return (bool)method.Invoke(instance, methodParameters)!;
	}

	/// <summary>
	///   Execute <see cref="IWebServiceExtension.OnLoad" /> method in the isolated context.
	/// </summary>
	/// <remarks>
	///   Matches parameter format of <see cref="IWebServiceExtension.OnLoad" /> method.
	/// </remarks>
	public static void Invoke(object instance, string methodName)
	{
		Type instanceType = instance.GetType();

		MethodInfo method = instanceType.GetMethod(methodName, MethodSearchFlags, null, [], null)!;

		_ = method.Invoke(instance, [])!;
	}

	/// <summary>
	///   Execute <see cref="IExplorerExtension.CommandSites" /> method in the isolated context.
	/// </summary>
	/// <remarks>
	///   Matches parameter format of <see cref="IExplorerExtension.CommandSites" /> method.
	/// </remarks>
	public static IEnumerable<T>? Invoke<T>(object instance, string methodName)
	{
		Type instanceType = instance.GetType();

		MethodInfo method = instanceType.GetMethod(methodName, MethodSearchFlags, null, [], null)!;

		return (IEnumerable<T>?)method.Invoke(instance, [])!;
	}
}
#endif
```


### Add `ResolveHelper.cs`

This is a helper class used to resolve conflicting versions of a dependency.

``` csharp
﻿using System.Reflection;
#if NETCOREAPP
using System.Runtime.Loader;
#endif

namespace IsolatedVaultAddin.Isolation;

/// <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;

		string 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 `Private` to `False` on Vault-related assembly file reference.
- Replace `VaultVersion` value of `2026` with the version of Vault being referenced.

``` xml
<!-- Custom Variable -->
<PropertyGroup>
	<VaultVersion>2026</VaultVersion>
</PropertyGroup>

<PropertyGroup Condition="$(VaultVersion) >= 2026">
	<DefineConstants>$(DefineConstants);VAULT_HAS_DOCK_PANELS</DefineConstants>
</PropertyGroup>

<ItemGroup>
	<Reference Include="Autodesk.Connectivity.Explorer.Extensibility">
		<HintPath>$(ProgramFiles)\Autodesk\Vault Client $(VaultVersion)\Explorer\Autodesk.Connectivity.Explorer.Extensibility.dll</HintPath>
		<!-- Required For Isolated Addin -->
		<Private>False</Private>
	</Reference>
	<Reference Include="Autodesk.Connectivity.Extensibility.Framework">
		<HintPath>$(ProgramFiles)\Autodesk\Vault Client $(VaultVersion)\Explorer\Autodesk.Connectivity.Extensibility.Framework.dll</HintPath>
		<!-- Required For Isolated Addin -->
		<Private>False</Private>
	</Reference>
	<Reference Include="Autodesk.Connectivity.JobProcessor.Extensibility">
		<HintPath>$(ProgramFiles)\Autodesk\Vault Client $(VaultVersion)\Explorer\Autodesk.Connectivity.JobProcessor.Extensibility.dll</HintPath>
		<!-- Required For Isolated Addin -->
		<Private>False</Private>
	</Reference>
	<Reference Include="Autodesk.Connectivity.WebServices">
		<HintPath>$(ProgramFiles)\Autodesk\Vault Client $(VaultVersion)\Explorer\Autodesk.Connectivity.WebServices.dll</HintPath>
		<!-- Required For Isolated Addin -->
		<Private>False</Private>
	</Reference>
	<Reference Include="Autodesk.DataManagement.Client.Framework">
		<HintPath>$(ProgramFiles)\Autodesk\Vault Client $(VaultVersion)\Explorer\Autodesk.DataManagement.Client.Framework.dll</HintPath>
		<!-- Required For Isolated Addin -->
		<Private>False</Private>
	</Reference>
	<Reference Include="Autodesk.DataManagement.Client.Framework.Vault">
		<HintPath>$(ProgramFiles)\Autodesk\Vault Client $(VaultVersion)\Explorer\Autodesk.DataManagement.Client.Framework.Vault.dll</HintPath>
		<!-- Required For Isolated Addin -->
		<Private>False</Private>
	</Reference>
</ItemGroup>
```


## Notes

- This add-in format is compatible with Vault versions using .NET Framework 4.8 or .NET 8.


## References

- Microsoft Learn - [System.Runtime.Loader.AssemblyLoadContext](https://learn.microsoft.com/en-us/dotnet/core/dependency-loading/understanding-assemblyloadcontext)
- GitHub - dotnet - [AssemblyLoadContext.md](https://github.com/dotnet/coreclr/blob/v2.1.0/Documentation/design-docs/assemblyloadcontext.md)
