Beispiel #1
0
        /// <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);
        }