Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot resolve scoped service

I have problem with understanding source of errors in my code. I try to get throw course about microservices in .net core. After running build solution I get:

------- Project finished: CrossX.Services.Identity. Succeeded: True. Errors: 0. Warnings: 0

But when I run it I get:

/opt/dotnet/dotnet /RiderProjects/crossx/src/CrossX.Services.Identity/bin/Debug/netcoreapp2.2/CrossX.Services.Identity.dll

Unhandled Exception: System.InvalidOperationException: Cannot resolve scoped service 'CrossX.NETCore.Commands.ICommandHandler`1[CrossX.NETCore.Commands.CreateUser]' from root provider.
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteValidator.ValidateResolution(Type serviceType, IServiceScope scope, IServiceScope rootScope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
   at CrossX.NETCore.Services.ServiceHost.BusBuilder.SubscribeToCommand[TCommand]() in /RiderProjects/crossx/src/CrossX.NETCore/Services/ServiceHost.cs:line 78
   at CrossX.Services.Identity.Program.Main(String[] args) in /RiderProjects/crossx/src/CrossX.Services.Identity/Program.cs:line 11

When I added to webHostBuilder .UseDefaultServiceProvider(options => options.ValidateScopes = false) my problem was solved. But turning off validations isn't good idea from what I know. Also When I changed AddScope to AddTransient problem was solved (or at least it run).

Problem is that I have no idea where to look for source of this error. I guess I lack of understanding what is wrong, so I would appreciate if someone would help me, or at least give a hint.

Here is my

Startup.cs:

public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
            services.AddRabbitMq(Configuration);
            services.AddScoped<ICommandHandler<CreateUser>, CreateUserHandler>();       
            services.AddScoped<IEncrypter, Encrypter>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseMvc();
        }
    }

Program.cs

public class Program
    {
        public static void Main(string[] args)
        {
            ServiceHost.Create<Startup>(args)
                .UseRabbitMq()
                .SubscribeToCommand<CreateUser>()
                .Build()
                .Run();
        }
    }

ServiceHost.cs

public class ServiceHost : IServiceHost
    {
        private readonly IWebHost _webHost;

        public ServiceHost(IWebHost webHost)
        {
            _webHost = webHost;
        }

        public void Run() => _webHost.Run();

        public static HostBuilder Create<TStartup>(string[] args) where TStartup : class
        {
            Console.Title = typeof(TStartup).Namespace;
            var config = new ConfigurationBuilder()
                .AddEnvironmentVariables()
                .AddCommandLine(args)
                .Build();
            var webHostBuilder = WebHost.CreateDefaultBuilder(args)
                .UseConfiguration(config)
//                .UseDefaultServiceProvider(options => options.ValidateScopes = false)
                .UseStartup<TStartup>();

            return new HostBuilder(webHostBuilder.Build());
        }

        public abstract class BuilderBase
        {
            public abstract ServiceHost Build();
        }

        public class HostBuilder : BuilderBase
        {
            private readonly IWebHost _webHost;
            private IBusClient _bus;

            public HostBuilder(IWebHost webHost)
            {
                _webHost = webHost;
            }

            public BusBuilder UseRabbitMq()
            {
                _bus = (IBusClient) _webHost.Services.GetService(typeof(IBusClient));
                return new BusBuilder(_webHost, _bus);
            }

            public override ServiceHost Build()
            {
                return new ServiceHost(_webHost);
            }
        }

        public class BusBuilder : BuilderBase
        {
            private readonly IWebHost _webHost;
            private IBusClient _bus;

            public BusBuilder(IWebHost webHost, IBusClient bus)
            {
                _webHost = webHost;
                _bus = bus;
            }

            public BusBuilder SubscribeToCommand<TCommand>() where TCommand : ICommand
            {
                var handler = (ICommandHandler<TCommand>) _webHost.Services.GetService(typeof(ICommandHandler<TCommand>));
                _bus.WithCommandHandlerAsync(handler);

                return this;
            }

            public BusBuilder SubscribeToEvent<TEvent>() where TEvent : IEvent
            {
                var handler = (IEventHandler<TEvent>) _webHost.Services.GetService(typeof(IEventHandler<TEvent>));
                _bus.WithEventHandlerAsync(handler);

                return this;
            }

            public override ServiceHost Build()
            {
                return new ServiceHost(_webHost);
            }
        }
    }
like image 719
Thou Avatar asked Mar 10 '19 16:03

Thou


People also ask

What is a scoped service?

In a scoped service, with every HTTP request, we get a new instance. However, within the same HTTP request, if the service is required in multiple places, like in the view and in the controller, then the same instance is provided for the entire scope of that HTTP request.

What is scoped service in .NET core?

In Asp.Net Core each request has in own service scope. Database and repository services are often registered as scoped services. Default registration of DbContext in EntityFramework Core is also scoped. Scoped lifetime ensures that all the services created within the request shares the same DbContext.

Can not consume scoped service from singleton?

You should almost never consume scoped service or transient service from a singleton. You should also avoid consuming transient service from a scoped service. What is happening when you consume scoped service from a singleton service is known as a captive dependency.

What is the difference between singleton and scoped?

Scoped lifetime services are created once per request. Singleton lifetime services are created the first time they are requested (or when ConfigureServices is run if you specify an instance there) and then every subsequent request will use the same instance.


1 Answers

Cannot resolve scoped service ICommandHandler<CreateUser> from root provider

As the error says, you cannot create a scoped service instance from the root provider. The root provider is the root service provider that exists outside of service scopes. As such, it cannot resolve services that should only be consumed within service scopes.

If you want to resolve a scoped service from the root provider, for example when you are consuming it from a singleton service, you should create a service scope first using the IServiceScopeFactory:

var serviceScopeFactory = _webHost.Services.GetService<IServiceScopeFactory>();
using (var scope = serviceScopeFactory.CreateScope())
{
    var handler = (IEventHandler<TEvent>)scope.ServiceProvider.GetService(typeof(IEventHandler<TEvent>))
    // …
}

Note that service scopes are supposed to be short lived, and that you need to dispose them afterwards to clean up.


Looking at your implementation, it seems as if you pass your scoped services to some other service in order to subscribe to events. This generally seems like a bad idea since that means that a reference to a scoped service will be kept by a (likely) singleton service for the whole lifetime of the application. This is generally a bad idea (as I said, scoped services are supposed to live only a short time).

You should ask yourself why you need the services to be scoped there, and whether you cannot make them singleton instead. Or if you actually need the subscribe mechanism to be based on the instance (instead of for example just the type, using a factory pattern or something).

like image 162
poke Avatar answered Nov 09 '22 23:11

poke