受欢迎的博客标签

Cannot resolve scoped service from root provider singleton

Published

Issue Description

.NET Core runtime gives error,

1.System.InvalidOperationException- Cannot resolve scoped service ” from root provider.
2. Cannot consume scoped service 'Nop.Services.Stores.IStoreMappingService' from singleton

Resolution

The error seems could occur at multiple places depending on your specific implementation while using the services in your code base.

As we know one can register the Services as Singleton or Scoped or Transient. While scope service instances are create once per request it is often recommended they are resolved using scoped instance of Application Builder. Then followed by using service provider one can resolve the dependencies.

You might need to consider below aspects also before resolving the issue.

How a lifetime of service instances is registered.
How the instance is initialized.
How the instance is resolved.

I was able to resolve the issue using the scoped service provider instance using the below code base. One can take a similar approach to resolve their specific issues.

var scope = app.ApplicationServices.CreateScope();
var service = scope.ServiceProvider.GetService<IUniqueIdService>();

 

for example:

Cannot resolve scoped service from root provider in Middleware

Middleware is always a singleton so you can't have scoped dependencies as constructor dependencies in the constructor of your middleware.

Middleware supports method injection on the Invoke method,so you can just add the IEmailRepository emailRepository as a parameter to that method and it will be injected there and will be fine as scoped.

our middleware and the service has to be compatible with each other in order to inject the service via the constructor of your middleware. Here, your middleware has been created as a convention-based middleware which means it acts as a singleton service and you have created your service as scoped-service. So, you cannot inject a scoped-service into the constructor of a singleton-service because it forces the scoped-service to act as a singleton one. However, here are your options.

1.Inject your service as a parameter to the InvokeAsync method.

see below

detail:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-5.0#lifetime-and-registration-options
2.Make your service a singleton one, if possible.


3.Transform your middleware to a factory-based one.

see:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/extensibility?view=aspnetcore-5.0

1.Inject your service as a parameter to the InvokeAsync method.

            //services
            services.AddScoped<INopLogger, DefaultLogger>();
            services.AddScoped<IWorkContext, WebWorkContext>();
            services.AddScoped<IEmailRepository, EmailRepository>();

error

  public class ErrorLoggingMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly INopLogger _logger;
        private readonly DiagnosticSource _diagnosticSource;
        private readonly IWorkContext _workContext;

        public ErrorLoggingMiddleware(RequestDelegate next, DiagnosticSource diagnosticSource, INopLogger logger )
        {
            if (next == null)
            {
                throw new ArgumentNullException(nameof(next));
            }
       

            _next = next;
            _diagnosticSource = diagnosticSource;
           

            this._logger = logger;


        }

        public async Task InvokeAsync(HttpContext context, IWorkContext workContext)
        {
            try
            {
                await _next(context);
            }
            catch (Exception ex)
            {
                await HandleExceptionAsync(context, ex);
               
                //Don't stop the error,rethrow the exception to show the error page
                ExceptionDispatchInfo.Throw(ex);
            }
        }

Scoped services must be resolved in the InvokeAsync method:

correct

  public class ErrorLoggingMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly INopLogger _logger;
        private readonly DiagnosticSource _diagnosticSource;
        private readonly IWorkContext _workContext;

        public ErrorLoggingMiddleware(RequestDelegate next, DiagnosticSource diagnosticSource)
        {
            if (next == null)
            {
                throw new ArgumentNullException(nameof(next));
            }
       

            _next = next;
            _diagnosticSource = diagnosticSource;
           

            


        }

        public async Task InvokeAsync(HttpContext context, IWorkContext workContext, INopLogger logger)
        {
            
            try
            {
                await _next(context);
            }
            catch (Exception ex)
            {
                await HandleExceptionAsync(context, ex, logger);
               
                //Don't stop the error,rethrow the exception to show the error page
                ExceptionDispatchInfo.Throw(ex);
            }
        }

 

Another way to get the instance of scoped dependency is to inject service provider (IServiceProvider) into the middleware constructor, create scope in Invoke method and then get the required service from the scope:

using (var scope = _serviceProvider.CreateScope()) {
    var _emailRepository = scope.ServiceProvider.GetRequiredService<IEmailRepository>();

    //do your stuff....
}