fix: filter domain assembly scans to Default ALC to prevent type identity mismatches
Plugin assemblies loaded via PluginHost into isolated AssemblyLoadContexts produce distinct types even from the same DLL. When AppDomain.GetAssemblies() returns both Default and plugin-ALC copies, DI registration and IOptions<T> resolution silently fail (e.g. ValkeyTransportOptions defaulting to localhost). Applied AssemblyLoadContext.Default filter to all 7 assembly discovery sites: - MessagingServiceCollectionExtensions (transport plugin scan) - StellaRouterIntegrationHelper (transport plugin loader) - Gateway.WebService Program.cs (startup transport scan) - GeneratedEndpointDiscoveryProvider (endpoint provider scan) - ReflectionEndpointDiscoveryProvider (endpoint attribute scan) - ServiceCollectionExtensions (schema provider scan) - MigrationModulePluginDiscovery (migration plugin scan) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,7 @@ using StellaOps.Router.Gateway.Middleware;
|
||||
using StellaOps.Router.Gateway.OpenApi;
|
||||
using StellaOps.Router.Gateway.RateLimit;
|
||||
using StellaOps.Router.Gateway.Routing;
|
||||
using System.Runtime.Loader;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
@@ -77,7 +78,8 @@ var transportPluginLoader = new RouterTransportPluginLoader(
|
||||
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()
|
||||
.Where(assembly =>
|
||||
assembly.GetName().Name?.StartsWith("StellaOps.Router.Transport.", StringComparison.OrdinalIgnoreCase) == true))
|
||||
assembly.GetName().Name?.StartsWith("StellaOps.Router.Transport.", StringComparison.OrdinalIgnoreCase) == true
|
||||
&& AssemblyLoadContext.GetLoadContext(assembly) == AssemblyLoadContext.Default))
|
||||
{
|
||||
transportPluginLoader.LoadFromAssembly(assembly);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using Microsoft.Extensions.Logging;
|
||||
using StellaOps.Messaging.Abstractions;
|
||||
using StellaOps.Messaging.Plugins;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
|
||||
namespace StellaOps.Messaging.DependencyInjection;
|
||||
|
||||
@@ -33,9 +34,12 @@ public static class MessagingServiceCollectionExtensions
|
||||
var loader = new MessagingPluginLoader();
|
||||
var plugins = loader.LoadFromDirectory(options.PluginDirectory, options.SearchPattern);
|
||||
|
||||
// Also load from assemblies in the current domain that might contain plugins
|
||||
// Also load from assemblies in the current domain that might contain plugins.
|
||||
// Filter to Default ALC only — plugin assemblies loaded via PluginHost into isolated
|
||||
// AssemblyLoadContexts have distinct types that cause IOptions<T> resolution failures.
|
||||
var domainAssemblies = AppDomain.CurrentDomain.GetAssemblies()
|
||||
.Where(a => a.GetName().Name?.StartsWith("StellaOps.Messaging.Transport.") == true);
|
||||
.Where(a => a.GetName().Name?.StartsWith("StellaOps.Messaging.Transport.") == true
|
||||
&& AssemblyLoadContext.GetLoadContext(a) == AssemblyLoadContext.Default);
|
||||
var domainPlugins = loader.LoadFromAssemblies(domainAssemblies);
|
||||
|
||||
var allPlugins = plugins.Concat(domainPlugins)
|
||||
|
||||
@@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StellaOps.Router.Common.Models;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
|
||||
namespace StellaOps.Microservice;
|
||||
|
||||
@@ -98,7 +99,9 @@ public sealed class GeneratedEndpointDiscoveryProvider : IEndpointDiscoveryProvi
|
||||
}
|
||||
|
||||
// Check all loaded assemblies (integration tests may host multiple microservices in one process).
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
// Filter to Default ALC to avoid type identity mismatches from isolated plugin ALCs.
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()
|
||||
.Where(a => AssemblyLoadContext.GetLoadContext(a) == AssemblyLoadContext.Default))
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StellaOps.Router.Common.Models;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
|
||||
namespace StellaOps.Microservice;
|
||||
|
||||
@@ -28,7 +29,9 @@ public sealed class ReflectionEndpointDiscoveryProvider : IEndpointDiscoveryProv
|
||||
ILogger? logger = null)
|
||||
{
|
||||
_options = options;
|
||||
_assemblies = assemblies ?? AppDomain.CurrentDomain.GetAssemblies();
|
||||
// Filter to Default ALC to avoid type identity mismatches from isolated plugin ALCs.
|
||||
_assemblies = assemblies ?? AppDomain.CurrentDomain.GetAssemblies()
|
||||
.Where(a => AssemblyLoadContext.GetLoadContext(a) == AssemblyLoadContext.Default);
|
||||
_serviceProviderIsService = serviceProviderIsService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using StellaOps.Microservice.Validation;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
|
||||
namespace StellaOps.Microservice;
|
||||
|
||||
@@ -164,7 +165,9 @@ public static class ServiceCollectionExtensions
|
||||
|
||||
private static Type? FindGeneratedSchemaProvider(List<SchemaProviderDiscoveryIssue> issues)
|
||||
{
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
// Filter to Default ALC to avoid type identity mismatches from isolated plugin ALCs.
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()
|
||||
.Where(a => AssemblyLoadContext.GetLoadContext(a) == AssemblyLoadContext.Default))
|
||||
{
|
||||
Type[] types;
|
||||
try
|
||||
|
||||
@@ -4,6 +4,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using StellaOps.Router.Common.Enums;
|
||||
using StellaOps.Router.Common.Plugins;
|
||||
using System.Globalization;
|
||||
using System.Runtime.Loader;
|
||||
|
||||
namespace StellaOps.Router.AspNet;
|
||||
|
||||
@@ -492,10 +493,13 @@ public static class StellaRouterIntegrationHelper
|
||||
{
|
||||
var loader = new RouterTransportPluginLoader();
|
||||
|
||||
// Filter to Default ALC only — assemblies loaded via PluginHost into isolated
|
||||
// AssemblyLoadContexts have distinct types that cause DI resolution failures.
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()
|
||||
.Where(a => a.GetName().Name?.StartsWith(
|
||||
"StellaOps.Router.Transport.",
|
||||
StringComparison.OrdinalIgnoreCase) == true))
|
||||
"StellaOps.Router.Transport.",
|
||||
StringComparison.OrdinalIgnoreCase) == true
|
||||
&& AssemblyLoadContext.GetLoadContext(a) == AssemblyLoadContext.Default))
|
||||
{
|
||||
loader.LoadFromAssembly(assembly);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user