How to overwrite a scoped service with a decorated implementation?

后端 未结 5 599
日久生厌
日久生厌 2020-12-18 06:07

I\'m trying to write an ASP.NET Core 2.2 integration test, where the test setup decorates a specific service that would normally be available to the API as a dependency. The

5条回答
  •  爱一瞬间的悲伤
    2020-12-18 06:51

    All the other answers were very helpful:

    • @ChrisPratt clearly explains the underlying problem, and offers a solution where Startup makes the service registration virtual and then overrides that in a TestStartup that is forced upon the IWebHostBuilder
    • @huysentruitw answers as well that this is a limitation of the underlying default DI container
    • @KirkLarkin offers a pragmatic solution where you register BarService itself in Startup and then use that to overwrite the IBarService registration completely

    And still, I'd like to offer yet another answer.

    The other answers helped me find the right terms to Google for. Turns out, there is the "Scrutor" NuGet package which adds the needed decorator support to the default DI container. You can test this solution yourself as it simply requires:

    builder.ConfigureTestServices(servicesConfiguration =>
    {
        // Requires "Scrutor" from NuGet:
        servicesConfiguration.Decorate();
    });
    

    Mentioned package is open source (MIT), and you can also just adapt only the needed features yourself, thus answering the original question as it stood, without external dependencies or changes to anything except the test project:

    public class IntegrationTestsFixture : WebApplicationFactory
    {
        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            base.ConfigureWebHost(builder);
    
            builder.ConfigureTestServices(servicesConfiguration =>
            {
                // The chosen solution here is adapted from the "Scrutor" NuGet package, which
                // is MIT licensed, and can be found at: https://github.com/khellang/Scrutor
                // This solution might need further adaptation for things like open generics...
    
                var descriptor = servicesConfiguration.Single(s => s.ServiceType == typeof(IBarService));
    
                servicesConfiguration.AddScoped(di 
                    => new DecoratedBarService(GetInstance(di, descriptor)));
            });
        }
    
        // Method loosely based on Scrutor, MIT licensed: https://github.com/khellang/Scrutor/blob/68787e28376c640589100f974a5b759444d955b3/src/Scrutor/ServiceCollectionExtensions.Decoration.cs#L319
        private static T GetInstance(IServiceProvider provider, ServiceDescriptor descriptor)
        {
            if (descriptor.ImplementationInstance != null)
            {
                return (T)descriptor.ImplementationInstance;
            }
    
            if (descriptor.ImplementationType != null)
            {
                return (T)ActivatorUtilities.CreateInstance(provider, descriptor.ImplementationType);
            }
    
            if (descriptor.ImplementationFactory != null)
            {
                return (T)descriptor.ImplementationFactory(provider);
            }
    
            throw new InvalidOperationException($"Could not create instance for {descriptor.ServiceType}");
        }
    }
    

提交回复
热议问题