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

Information
Class: Microsoft.Extensions.DependencyInjection.MongoDbAsyncResponseChannelServiceCollectionExtensions
Assembly: AsyncResponse.Channels.MongoDB
File(s): /_/src/Channels/AsyncResponse.Channels.MongoDB/ServiceCollectionExtensions.cs
Line coverage
100%
Covered lines: 55
Uncovered lines: 0
Coverable lines: 55
Total lines: 99
Line coverage: 100%
Branch coverage
91%
Covered branches: 11
Total branches: 12
Branch coverage: 91.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
WithMongoDbChannel(...)91.66%1212100%

File(s)

/_/src/Channels/AsyncResponse.Channels.MongoDB/ServiceCollectionExtensions.cs

#LineLine coverage
 1using AsyncResponse;
 2using AsyncResponse.Channels.MongoDB;
 3using AsyncResponse.Internal;
 4using Microsoft.Extensions.DependencyInjection.Extensions;
 5using Microsoft.Extensions.Logging;
 6using Microsoft.Extensions.Options;
 7using MongoDB.Driver;
 8
 9namespace Microsoft.Extensions.DependencyInjection;
 10
 11/// <summary>DI registration for the MongoDB-backed AsyncResponse channel package.</summary>
 12public static class MongoDbAsyncResponseChannelServiceCollectionExtensions
 13{
 14    /// <summary>
 15    /// Registers MongoDB change streams as the response channel — behind
 16    /// <see cref="IAsyncResponsePublisher"/>, <see cref="IAsyncResponseSubscriber"/>, and
 17    /// <see cref="IRecoverableAsyncResponseSubscriber"/> — together with the durable TTL-indexed
 18    /// MongoDB recovery-state store. Hosts may register an <see cref="IMongoDatabase"/> (or an
 19    /// <see cref="IMongoClient"/> plus <see cref="MongoDbAsyncResponseChannelOptions.DatabaseName"/>)
 20    /// singleton, or set <see cref="MongoDbAsyncResponseChannelOptions.ConnectionString"/> here.
 21    /// Change streams require the server to run as a replica set (single-node is sufficient).
 22    /// </summary>
 23    public static AsyncResponseRegistrationBuilder WithMongoDbChannel(
 24        this AsyncResponseRegistrationBuilder builder,
 25        Action<MongoDbAsyncResponseChannelOptions>? configure = null)
 26    {
 39227        var services = builder.Services;
 39228        services.AddOptions();
 39229        if (configure is not null)
 38630            services.Configure(configure);
 31
 32        // The store reuses a host-registered IMongoDatabase / IMongoClient when present; otherwise it
 33        // creates and owns a client from the options. Nothing is registered as a bare
 34        // IMongoClient/IMongoDatabase service, so unrelated resolutions of those types are never
 35        // answered — or broken — by this package.
 36        // One registry per container: DI-hosted Mongo components claim their effective
 37        // collections so a cross-component collision (e.g. a durable-flow store on the channel's
 38        // derived counters collection) fails startup in either construction order.
 39239        services.TryAddSingleton<IMongoNamespaceRegistry, MongoNamespaceRegistry>();
 39240        services.TryAddSingleton(provider =>
 39241        {
 38842            var options = provider.GetRequiredService<IOptions<MongoDbAsyncResponseChannelOptions>>();
 38843            var registry = provider.GetRequiredService<IMongoNamespaceRegistry>();
 38844            var logger = provider.GetService<ILogger<MongoDbChannelStore>>();
 39245
 38846            var database = provider.GetService<IMongoDatabase>();
 38847            if (database is not null)
 34048                return new MongoDbChannelStore(database, options, namespaceRegistry: registry, logger: logger);
 39249
 4850            if (string.IsNullOrWhiteSpace(options.Value.DatabaseName))
 251                throw new InvalidOperationException($"{nameof(MongoDbAsyncResponseChannelOptions)}.{nameof(MongoDbAsyncR
 39252
 4653            var sharedClient = provider.GetService<IMongoClient>();
 4654            if (sharedClient is not null)
 4255                return new MongoDbChannelStore(sharedClient.GetDatabase(options.Value.DatabaseName), options, namespaceR
 39256
 457            if (string.IsNullOrWhiteSpace(options.Value.ConnectionString))
 258                throw new InvalidOperationException($"{nameof(MongoDbAsyncResponseChannelOptions)}.{nameof(MongoDbAsyncR
 39259
 260            var ownedClient = new MongoClient(options.Value.ConnectionString);
 261            return new MongoDbChannelStore(ownedClient.GetDatabase(options.Value.DatabaseName), options, ownedClient, na
 39262        });
 63
 39264        services.TryAddSingleton<MongoDbRecoveryStateStore>();
 76465        services.Replace(ServiceDescriptor.Singleton<IRecoveryStateStore>(provider => provider.GetRequiredService<MongoD
 72466        services.Replace(ServiceDescriptor.Singleton<IRecoveryStateScanner>(provider => provider.GetRequiredService<Mong
 67
 39268        services.TryAddSingleton<MongoDbAsyncResponseChannel>();
 75269        services.Replace(ServiceDescriptor.Singleton<IAsyncResponsePublisher>(provider => provider.GetRequiredService<Mo
 70570        services.Replace(ServiceDescriptor.Singleton<IRawAsyncResponsePublisher>(provider => provider.GetRequiredService
 75571        services.Replace(ServiceDescriptor.Singleton<IAsyncResponseSubscriber>(provider => provider.GetRequiredService<M
 72572        services.Replace(ServiceDescriptor.Singleton<IRecoverableAsyncResponseSubscriber>(provider => provider.GetRequir
 72973        services.Replace(ServiceDescriptor.Singleton<IActiveSubscriberProbe>(provider => provider.GetRequiredService<Mon
 74
 72475        services.Replace(ServiceDescriptor.Singleton<IRecoverableAsyncResponseBuilder>(provider => new RecoverableAsyncR
 72476            provider.GetRequiredService<IRecoverableAsyncResponseSubscriber>(),
 72477            provider.GetService<IWorkerTransport>(),
 72478            provider.GetService<IAsyncResponseReplyTargetProvider>(),
 72479            provider.GetRequiredService<AsyncResponseContextPropagation>(),
 72480            provider.GetService<TimeProvider>(),
 72481            // The producer-side mirror of the ingress's inbound size budget (WorkerJobTooLargeException).
 72482            provider.GetService<IOptions<AsyncResponseOptions>>())));
 72483        services.Replace(ServiceDescriptor.Singleton<IAsyncResponseBuilder>(provider => provider.GetRequiredService<IRec
 84
 85        // The resolved default waiter timeout is declared through the marker so the startup
 86        // validator can require the durable-flow ledger TTL to out-live a timeout-less awaited
 87        // step, and the flow engine can extend a parked ledger by it — without either referencing
 88        // channel option types.
 39289        services.AddSingleton(provider =>
 39290        {
 33491            var options = provider.GetRequiredService<Microsoft.Extensions.Options.IOptions<MongoDbAsyncResponseChannelO
 33492            return new AsyncResponseChannelMarker(MongoDbAsyncResponseChannelOptions.ChannelName)
 33493            {
 33494                EffectiveDefaultWaitTimeout = options.DefaultTimeout ?? options.RecoveryStateExpiry
 33495            };
 39296        });
 39297        return builder;
 98    }
 99}