< Summary

Information
Class: NexMediator.Core.Internal.NexStreamExecutor
Assembly: NexMediator.Core
File(s): /home/runner/work/NexMediator/NexMediator/src/NexMediator.Core/Internal/NexStreamExecutor.cs
Line coverage
100%
Covered lines: 52
Uncovered lines: 0
Coverable lines: 52
Total lines: 97
Line coverage: 100%
Branch coverage
75%
Covered branches: 6
Total branches: 8
Branch coverage: 75%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
Execute(...)100%22100%
CastAsync()66.66%66100%

File(s)

/home/runner/work/NexMediator/NexMediator/src/NexMediator.Core/Internal/NexStreamExecutor.cs

#LineLine coverage
 1using Microsoft.Extensions.DependencyInjection;
 2using NexMediator.Abstractions.Interfaces;
 3using System.Collections.Concurrent;
 4using System.Linq.Expressions;
 5using System.Runtime.CompilerServices;
 6
 7namespace NexMediator.Core.Internal;
 8
 9/// <summary>
 10/// Executes stream requests using compiled delegates per request/response types.
 11///
 12/// Optimizations:
 13/// - Compiles expressions to avoid reflection at runtime.
 14/// - Caches executor delegates by request and response types.
 15/// - Avoids dynamic casting overhead.
 16/// </summary>
 17internal static class NexStreamExecutor
 18{
 119    private static readonly ConcurrentDictionary<(Type requestType, Type responseType),
 120        Func<object, IServiceProvider, CancellationToken, IAsyncEnumerable<object>>> _cache =
 121        new();
 22
 23    /// <summary>
 24    /// Executes a stream request and returns an async sequence of responses.
 25    /// </summary>
 26    /// <typeparam name="TResponse">Type of each response item.</typeparam>
 27    /// <param name="request">The stream request to execute.</param>
 28    /// <param name="provider">Service provider for resolving the handler.</param>
 29    /// <param name="cancellationToken">Token to cancel the operation.</param>
 30    /// <returns>An async enumerable of <typeparamref name="TResponse"/> items.</returns>
 31    public static IAsyncEnumerable<TResponse> Execute<TResponse>(
 32        INexStreamRequest<TResponse> request,
 33        IServiceProvider provider,
 34        CancellationToken cancellationToken)
 335    {
 336        var requestType = request.GetType();
 337        var key = (requestType, typeof(TResponse));
 38
 339        var executor = _cache.GetOrAdd(key, static tuple =>
 240        {
 241            var (reqType, resType) = tuple;
 242            var handlerType = typeof(INexStreamRequestHandler<,>)
 243                .MakeGenericType(reqType, resType);
 244            var handleMethod = handlerType
 245                .GetMethod(nameof(INexStreamRequestHandler<INexStreamRequest<object>, object>.Handle))!;
 346
 247            var requestParam = Expression.Parameter(typeof(object), "request");
 248            var providerParam = Expression.Parameter(typeof(IServiceProvider), "provider");
 249            var ctParam = Expression.Parameter(typeof(CancellationToken), "ct");
 350
 251            var castRequest = Expression.Convert(requestParam, reqType);
 252            var getHandler = Expression.Call(
 253                typeof(ServiceProviderServiceExtensions),
 254                nameof(ServiceProviderServiceExtensions.GetRequiredService),
 255                new[] { handlerType },
 256                providerParam);
 357
 258            var callHandle = Expression.Call(
 259                getHandler,
 260                handleMethod,
 261                castRequest,
 262                ctParam);
 363
 264            var convert = Expression.Convert(
 265                callHandle,
 266                typeof(IAsyncEnumerable<object>));
 367
 268            var lambda = Expression.Lambda<Func<object, IServiceProvider, CancellationToken, IAsyncEnumerable<object>>>(
 269                convert,
 270                requestParam,
 271                providerParam,
 272                ctParam);
 373
 274            return lambda.Compile();
 575        });
 76
 377        var result = executor(request!, provider, cancellationToken);
 278        return CastAsync<TResponse>(result, cancellationToken);
 279    }
 80
 81    /// <summary>
 82    /// Casts each object item in an async sequence to <typeparamref name="TResponse"/>.
 83    /// </summary>
 84    /// <typeparam name="TResponse">Target item type.</typeparam>
 85    /// <param name="source">Source sequence of objects.</param>
 86    /// <param name="ct">Token to cancel the iteration.</param>
 87    /// <returns>An async enumerable of <typeparamref name="TResponse"/> items.</returns>
 88    private static async IAsyncEnumerable<TResponse> CastAsync<TResponse>(
 89        IAsyncEnumerable<object> source,
 90        [EnumeratorCancellation] CancellationToken ct = default)
 291    {
 1192        await foreach (var item in source.WithCancellation(ct).ConfigureAwait(false))
 393        {
 394            yield return (TResponse)item!;
 395        }
 196    }
 97}