/// <summary>
        /// Returns the current service provider with mock service registrations for the specified test host service provider
        /// </summary>
        /// <param name="serviceProvider">Test host service provider</param>
        /// <returns></returns>
        public static IServiceProvider GetCurrentScopeServiceProvider(this IServiceProvider serviceProvider)
        {
            var currentScope = NestedServiceScope.GetCurrentScopeByServiceProvider(serviceProvider);

            return(currentScope == null ? serviceProvider : currentScope.ServiceProvider);
        }
        /// <summary>
        /// Decorate service registrations to allow local mock service registrations inside tests without rebuilding the DI-container
        /// This method should be called the last for the test host after all required service configuration calls.
        /// That's the reason why it returns void and not IServiceCollection, there aren't supposed to be any additional calls after that.
        /// </summary>
        /// <param name="services"></param>
        /// <param name="decorationCriteria">The criteria whether the given service should be decorated for possible local mock registrations</param>
        public static void DecorateServicesForTesting(this IServiceCollection services, Func <Type, bool> decorationCriteria)
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }

            if (decorationCriteria == null)
            {
                throw new ArgumentNullException(nameof(decorationCriteria));
            }

            //Register root service collection to be able to determine the current mock scope later on
            services.AddSingleton(services);

            services.Remove(new ServiceDescriptor(typeof(IOptions <>), typeof(OptionsManager <>),
                                                  ServiceLifetime.Singleton));
            services.Remove(new ServiceDescriptor(typeof(IOptionsSnapshot <>), typeof(OptionsManager <>),
                                                  ServiceLifetime.Scoped));
            services.Remove(new ServiceDescriptor(typeof(IOptionsMonitor <>), typeof(OptionsMonitor <>),
                                                  ServiceLifetime.Singleton));

            var configureOptions = services.Where(s => s.ServiceType.IsClosedTypeOf(typeof(IConfigureOptions <>)))
                                   .ToArray();

            var existingOptionsRegistrations         = services.Where(s => s.ServiceType.IsClosedTypeOf(typeof(IOptions <>))).ToArray();
            var existingOptionsSnapshotRegistrations = services.Where(s => s.ServiceType.IsClosedTypeOf(typeof(IOptionsSnapshot <>))).ToArray();
            var existingOptionsMonitorRegistrations  = services.Where(s => s.ServiceType.IsClosedTypeOf(typeof(IOptionsMonitor <>))).ToArray();

            foreach (var configureOption in configureOptions)
            {
                var optionsType = configureOption.ServiceType.GenericTypeArguments[0];

                var optionsClosedGenericType         = typeof(IOptions <>).MakeGenericType(optionsType);
                var optionsSnapshotClosedGenericType = typeof(IOptionsSnapshot <>).MakeGenericType(optionsType);
                var optionsMonitorClosedGenericType  = typeof(IOptionsMonitor <>).MakeGenericType(optionsType);

                if (existingOptionsRegistrations.All(s => s.ServiceType != optionsClosedGenericType))
                {
                    services.AddSingleton(optionsClosedGenericType,
                                          typeof(OptionsManager <>).MakeGenericType(optionsType));
                }

                if (existingOptionsSnapshotRegistrations.All(s => s.ServiceType != optionsSnapshotClosedGenericType))
                {
                    services.AddScoped(optionsSnapshotClosedGenericType,
                                       typeof(OptionsManager <>).MakeGenericType(optionsType));
                }

                if (existingOptionsMonitorRegistrations.All(s => s.ServiceType != optionsMonitorClosedGenericType))
                {
                    services.AddSingleton(optionsMonitorClosedGenericType,
                                          typeof(OptionsMonitor <>).MakeGenericType(optionsType));
                }
            }

            var newServices = new ServiceCollection();

            foreach (var wrappedServiceDescriptor in services)
            {
                Func <IServiceProvider, object> objectFactory = serviceProvider =>
                {
                    var currentScope = NestedServiceScope.GetCurrentScopeByRootServices(services);
                    if (currentScope == null)
                    {
                        return(serviceProvider.CreateInstance(wrappedServiceDescriptor));
                    }

                    var serviceDescriptor =
                        currentScope.GetClosestServiceDefinition(wrappedServiceDescriptor.ServiceType);
                    return(serviceDescriptor == null
                                                ? serviceProvider.CreateInstance(wrappedServiceDescriptor)
                                                : serviceProvider.CreateInstance(serviceDescriptor));
                };

                if (decorationCriteria(wrappedServiceDescriptor.ServiceType))
                {
                    //We can't mock classes and open-generic interfaces
                    if (wrappedServiceDescriptor.ServiceType.IsClass ||
                        wrappedServiceDescriptor.ServiceType.IsGenericType &&
                        wrappedServiceDescriptor.ServiceType.ContainsGenericParameters)
                    {
                        newServices.Add(wrappedServiceDescriptor);
                    }
                    else
                    {
                        //We change decorated services Singleton lifetimes to Scoped to make mocked registrations local
                        //to a concrete mock scope
                        var serviceLifetime = wrappedServiceDescriptor.Lifetime == ServiceLifetime.Singleton
                                                        ? ServiceLifetime.Scoped
                                                        : wrappedServiceDescriptor.Lifetime;
                        newServices.Add(ServiceDescriptor.Describe(wrappedServiceDescriptor.ServiceType, objectFactory, serviceLifetime));
                    }
                }
                else
                {
                    newServices.Add(wrappedServiceDescriptor);
                }
            }
            services.Clear();
            services.Add(newServices);
        }
Пример #3
0
 public NestedServiceProvider(IServiceProvider rootServiceProvider, NestedServiceScope nestedServiceScope)
 {
     RootServiceProvider = rootServiceProvider;
     NestedServiceScope  = nestedServiceScope;
 }