Skip to content

AssemblyLoadContext is unloading or was already unloaded. #75067

Open
@Seabizkit

Description

@Seabizkit

Description

My code fails to run when deployed, seem to work when running through VS

Details I have a plugin which im using AssemblyLoadContext and have marked as isCollectible: true
It all seem to work, but does not work when deployed.

every project is on .net 6 to ensure no other variables when trying to debug this AssemblyLoadContext and usage.

-Web
---- Hosts - BackgroundProcessor
-----------Kicks off Plugin code

Only code share with plugin is Component.Facade Lib which has no other references.(it works in VS).

Fails when trying to run plugin code.
I deploy this with self container and targeting Arm32 Release mode, a PI4 with raspberry OS (32bit)

update: confirmed works in Debug but not Release in VS

Exception message:
Could not load file or assembly 'Microsoft.Extensions.Hosting.Abstractions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'. An operation is not legal in the current state. (0x80131509)

Exception inner:
Could not load file or assembly 'Microsoft.Extensions.Hosting.Abstractions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'. An operation is not legal in the current state. (0x80131509) AssemblyLoadContext is unloading or was already unloaded.

Plugin has:
image

my guess the above is this is the issue, but how are you meant to handle this?

the class which kicks off the plugin stuff is TaskComponentProcessor

inside there I have

 var pluginInfo = await _taskComponentHelper.EnsureLocalPluginAsync(item.BuilderComponentId.Value);
  var pluginLocation = Path.GetFullPath(Path.Combine(pluginInfo.Path, pluginInfo.Name));
  var loadContext = new PluginLoadContext(pluginLocation);
  var componentUI = loadContext.GetImplementations<BaseComponentUI>().Single();
  var plugin = loadContext.GetImplementations<IParadoxComponent>().Single();

i then call

 var res = await plugin.ExcuteAsync(executingContext, ct);

code for PluginLoadContext

public class PluginLoadContext : AssemblyLoadContext
  {
      private AssemblyDependencyResolver _resolver;
      private string _path;
      public PluginLoadContext() : base(isCollectible: true)
      { }

      public PluginLoadContext(string pluginPath) : base(isCollectible: true)
      {
          _path = pluginPath;
          _resolver = new AssemblyDependencyResolver(pluginPath);
      }

      protected override Assembly Load(AssemblyName assemblyName)
      {
          if (_resolver != null)
          {
              string assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
              if (assemblyPath != null)
              {
                  return LoadFromAssemblyPath(assemblyPath);
              }
          }
          return null;
      }

      protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
      {
          string libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
          if (libraryPath != null)
          {
              return LoadUnmanagedDllFromPath(libraryPath);
          }

          return IntPtr.Zero;
      }

      public IEnumerable<T> GetImplementations<T>()
      {
          var type = typeof(T);
          var assName = AssemblyName.GetAssemblyName(_path);
          var assembly = LoadFromAssemblyName(assName);
          var types = assembly.GetTypes().ToList();
          var other = new List<Type>();
          if (type.IsAbstract && !type.IsInterface)
          {
              other = types.Where(m => m.IsClass && !m.IsAbstract
                                      && m.IsSubclassOf(type)
                              ).ToList();
          }
          else
          {
              other = types.Where(t => type.IsAssignableFrom(t)).ToList();
          }
          return other
              .Select(t => Activator.CreateInstance(t))
              .Cast<T>();

      }

Statcktrace:

 at Paradox.Component.FtpDownloadPlugin.DIBuilder.Start(ExecutingContext e, Action`1 configureDelegate) 
 at Paradox.Component.FtpDownloadPlugin.Plugin.SetupService(ExecutingContext e, IConfiguration configuration) 
in :\Projects\Paradox.Importer\src\ComponentContainer\ComponentFunctions\Paradox.Component.FtpDownload\Plugin.cs:line 31  at Paradox.Component.FtpDownloadPlugin.Plugin.ExcuteAsync(ExecutingContext ec, CancellationToken ct) 
in :\Projects\Paradox.Importer\src\ComponentContainer\ComponentFunctions\Paradox.Component.FtpDownload\Plugin.cs:line 58  at Paradox.Component.FtpDownloadPlugin.Plugin.ExcuteAsync(ExecutingContext ec, CancellationToken ct) 
in :\Projects\Paradox.Importer\src\ComponentContainer\ComponentFunctions\Paradox.Component.FtpDownload\Plugin.cs:line 93  at Paradox.ComponentCode.TaskComponentProcessor.PreformStepPluginAsync(Int32 actionRequestId, BuilderTaskRun taskRun, BuilderTaskRunItem item, ProgressDto progressDto, IArtifactManger storageManger, CancellationToken ct) 
in C:\Projects\Paradox.Importer\src\ComponentContainer\Paradox.ComponentCode\TaskComponentProcessor.cs:line 343 
at Paradox.ComponentCode.TaskComponentProcessor.PreformStepPluginAsync(Int32 actionRequestId, BuilderTaskRun taskRun, BuilderTaskRunItem item, ProgressDto progressDto, IArtifactManger storageManger, CancellationToken ct) 
in C:\Projects\Paradox.Importer\src\ComponentContainer\Paradox.ComponentCode\TaskComponentProcessor.cs:line 381 
 at Paradox.ComponentCode.TaskComponentProcessor.ExecuteAsync(Int32 actionRequestId, Int32 builderTaskRunId, CancellationToken ct) 
in C:\Projects\Paradox.Importer\src\ComponentContainer\Paradox.ComponentCode\TaskComponentProcessor.cs:line 164

Reproduction Steps

It work when running through VS so, not sure how to debug,
Open to suggestions.

in my plugin i have which i used to return a IServiceProvider so i can register any service the plugin may need.

public class DIBuilder
    {
        private const string DefaultEnviroementName = "Development";
        public static IServiceProvider Start(ExecutingContext e, Action<IServiceCollection> configureDelegate)
        {

            var configuration = SetConfigation(e);
            var host = CreateHostBuilder(configuration, e, configureDelegate).Build();
            return host.Services;
        }

        public static IConfiguration SetConfigation(ExecutingContext e)
        {
            //this should be local to the plugin and not the app: Hack
            var directory = Directory.GetCurrentDirectory();

            //  e.Logger.LogDebugAsync(directory);

            var environment = Environment.GetEnvironmentVariable("ASPNET_ENVIROMENT") ??
                      Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ??
                      DefaultEnviroementName;

            //  e.Logger.LogDebugAsync(environment);

            var builder = new ConfigurationBuilder()
                .SetBasePath(directory)
                .AddJsonFile($"appsettings.json", true, true)
                .AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true);

            IConfiguration configuration = builder.Build();
            return configuration;
        }

        public static IHostBuilder CreateHostBuilder(IConfiguration configuration, ExecutingContext e,
            Action<IServiceCollection> configureDelegate, string[] args = null)
        {
            return Host.CreateDefaultBuilder(args)
                      .ConfigureServices(configureDelegate);
        }
    }

Expected behavior

Should not through exception, or at least help with how to fix it.

Actual behavior

fails when deployed, giving the exception

Exception message:
Could not load file or assembly 'Microsoft.Extensions.Hosting.Abstractions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'. An operation is not legal in the current state. (0x80131509)

Exception inner:
Could not load file or assembly 'Microsoft.Extensions.Hosting.Abstractions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'. An operation is not legal in the current state. (0x80131509) AssemblyLoadContext is unloading or was already unloaded.

Regression?

No response

Known Workarounds

No response

Configuration

No response

Other information

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status

    No status

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions