/// <summary>
        /// Builds a <see cref="ServiceDescriptor"/>, describing a singleton <see cref="IHostedService"/>, to be used
        /// with dependency injection.
        /// </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 in the service factory wrapped by the
        /// <see cref="ServiceDescriptor"/>.
        /// </remarks>
        public Func <IServiceProvider, T> Build(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>)}.");
            }

            if (_configRabbitMqConfig == null)
            {
                _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
                })
                    .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> BuildServiceFactoryFactory()
            {
                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;
                    }
                });
            }

            return(BuildServiceFactoryFactory());
        }