/// <summary> /// Registers a subclass of <see cref="RabbitMqService{T}"/> to be used with dependency injection. /// /// If it is a consumer with type <c>TConsumer</c>, it is registered both as /// <see cref="RabbitMqConsumer{TConsumer}"/> and <c>TConsumer</c>. /// /// If it is a producer with type <c>TProducer</c>, it is registered both as /// <see cref="RabbitMqProducer{TProducer}"/> and <c>TProducer</c>. /// </summary> /// <remarks> /// It reuses an existing <see cref="IBusProxy"/> singleton, if one is found using the provided /// <see cref="IRabbitMqConfig.Id"/>. /// /// If not, a new <see cref="IBusProxy"/> singleton is registered. /// </remarks> public void Add(IServiceCollection serviceCollection) { var isConsumer = typeof(T).IsSubclassOf(typeof(RabbitMqConsumer <T>)); if (!isConsumer && !typeof(T).IsSubclassOf(typeof(RabbitMqProducer <T>))) { throw new Exception( $"{nameof(T)}, of type {typeof(T).FullName}, must be a subclass of " + $"{nameof(RabbitMqConsumer<T>)} or {nameof(RabbitMqProducer<T>)}."); } _configRabbitMqConfig ??= new RabbitMqConfig(); var busProxy = serviceCollection .Where(serviceDescriptor => serviceDescriptor.Lifetime == ServiceLifetime.Singleton && serviceDescriptor.ServiceType == typeof(IBusProxy)) .Select(serviceDescriptor => (IBusProxy)serviceDescriptor.ImplementationInstance) .FirstOrDefault(registeredBusProxy => registeredBusProxy.Id == _configRabbitMqConfig.Id); // ReSharper disable once ConditionIsAlwaysTrueOrFalse // ReSharper disable HeuristicUnreachableCode if (busProxy == null) { // build an IServiceProvider early so as to ensure that an ILogger<T> can be passed to // ConsumerErrorStrategy var serviceProvider = new DefaultServiceProviderFactory(new ServiceProviderOptions { ValidateScopes = false, ValidateOnBuild = false }) .CreateBuilder(serviceCollection) .BuildServiceProvider(); var bus = RabbitMqService <T> .CreateLazyBus( _configRabbitMqConfig, _configUseStronglyTypedMessages, _configUseCorrelationIds, serviceProvider); busProxy = new BusProxy(_configRabbitMqConfig.Id, bus); serviceCollection.AddSingleton(busProxy); } // ReSharper restore HeuristicUnreachableCode Func <IServiceProvider, T> ServiceFactoryFactory() { T service = null !; var @lock = new object(); return(serviceProvider => { // ReSharper disable once ConditionIsAlwaysTrueOrFalse if (service != null) { return service; } lock (@lock) { if (service != null) { return service; } // _configRabbitMqConfig must not be null here service = RabbitMqService <T> .Create <T>( isConsumer, busProxy, _configRabbitMqConfig !, _configOnConnected, serviceProvider); return service; } }); } var serviceFactory = ServiceFactoryFactory(); serviceCollection.AddHostedService(serviceFactory); var serviceType = isConsumer ? typeof(RabbitMqConsumer <T>) : typeof(RabbitMqProducer <T>); // enable injection as RabbitMqConsumer<MyConsumerType> or RabbitMqProducer<MyProducerType> serviceCollection.AddSingleton(serviceType, serviceFactory); // enable injection as MyConsumerType or MyProducerType serviceCollection.AddSingleton(serviceFactory); }