< Summary - AsyncResponse (Release / net8.0+net10.0 / unit+integration)

Information
Class: AsyncResponse.AsyncResponseTypeResolution
Assembly: AsyncResponse.Core
File(s): /home/runner/work/AsyncResponse/AsyncResponse/src/AsyncResponse.Core/AsyncResponseTypeResolution.cs
Line coverage
100%
Covered lines: 24
Uncovered lines: 0
Coverable lines: 24
Total lines: 81
Line coverage: 100%
Branch coverage
100%
Covered branches: 4
Total branches: 4
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
RegisterResolver(...)100%11100%
RegisterAssembly(...)100%11100%
Resolve(...)100%44100%
Reset()100%11100%

File(s)

/home/runner/work/AsyncResponse/AsyncResponse/src/AsyncResponse.Core/AsyncResponseTypeResolution.cs

#LineLine coverage
 1using System.Diagnostics.CodeAnalysis;
 2using System.Reflection;
 3
 4namespace AsyncResponse;
 5
 6/// <summary>
 7/// Opt-in extensibility for resolving the service and payload types named in persisted callbacks and
 8/// recovery state. By default AsyncResponse resolves a type name against the assemblies loaded into
 9/// the default <see cref="System.Runtime.Loader.AssemblyLoadContext"/>
 10/// (<c>AppDomain.CurrentDomain.GetAssemblies()</c>). Apps that load callback targets or payload types
 11/// into a <em>separate</em> <c>AssemblyLoadContext</c> — plugin hosts, dynamic-load scenarios — can
 12/// register an extra resolver (or assembly) here so those types resolve too, instead of the recovery
 13/// callback silently failing because the type was invisible to the default context.
 14/// <para>
 15/// This is process-wide and additive: registered resolvers are consulted only when the default scan
 16/// does not find the type. Registering nothing preserves the default behavior exactly.
 17/// </para>
 18/// </summary>
 19public static class AsyncResponseTypeResolution
 20{
 221    private static volatile Func<string, Type?>[] _resolvers = [];
 222    private static readonly object _gate = new();
 23
 24    /// <summary>
 25    /// Registers a custom resolver consulted (after the default assembly scan) when resolving a
 26    /// persisted type name. The resolver returns the resolved <see cref="Type"/> or <c>null</c>.
 27    /// </summary>
 28    public static void RegisterResolver(Func<string, Type?> resolver)
 29    {
 230        ArgumentNullException.ThrowIfNull(resolver);
 231        lock (_gate)
 32        {
 233            _resolvers = [.. _resolvers, resolver];
 234        }
 35
 36        // A new resolver can turn previously-unresolvable names into hits; drop the negative cache.
 237        ReflectionExtensions.InvalidateUnresolvableServiceTypes();
 238    }
 39
 40    /// <summary>
 41    /// Registers an assembly (typically one loaded into a non-default <c>AssemblyLoadContext</c>) to
 42    /// be searched for persisted type names.
 43    /// </summary>
 44    [RequiresUnreferencedCode("Resolves persisted type names against the assembly by string; a trimmed app may have remo
 45                              "those types. Plugin/dynamic-load scenarios are inherently incompatible with trimming the 
 46    public static void RegisterAssembly(Assembly assembly)
 47    {
 248        ArgumentNullException.ThrowIfNull(assembly);
 249        RegisterResolver(name => assembly.GetType(name, throwOnError: false));
 250    }
 51
 52    /// <summary>Consults the registered resolvers in order; returns the first non-null match, or <c>null</c>.</summary>
 53    internal static Type? Resolve(string fullName)
 54    {
 255        foreach (var resolver in _resolvers)
 56        {
 57            try
 58            {
 259                if (resolver(fullName) is { } type)
 260                    return type;
 261            }
 262            catch
 63            {
 64                // A misbehaving custom resolver must never break recovery resolution; skip to the next.
 265            }
 66        }
 67
 268        return null;
 269    }
 70
 71    /// <summary>Clears all registered resolvers. Test seam only.</summary>
 72    internal static void Reset()
 73    {
 274        lock (_gate)
 75        {
 276            _resolvers = [];
 277        }
 78
 279        ReflectionExtensions.InvalidateUnresolvableServiceTypes();
 280    }
 81}