0

I get the following exception:

System.InvalidOperationException: 'Cannot resolve scoped service 'OSPC.Shared.Core.Data.IRepository`1[OSPC.Configuration.Objects.SignalMetaData]' from root provider.' on the line ActivatorUtilities.CreateInstance in the CommandDispatcher class.

WHen investigating the parameter serviceProvider which is injected in the CommandDispatcher class I can see 122 items in the ResolvedServices collection (all microsoft related services) but none of my services which are registered in the ConfigureServices method are in the collection.

Why are my own registered services not appearing?

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<ILogManager, LogManager>();
        services.AddScoped<IDbContext>(c => new OSPCContext(Configuration.GetConnectionString("OSPCConnectionstring")));
        services.AddScoped(typeof(IRepository<>), typeof(EfRepository<>));
        services.AddSingleton<ICommandDispatcher, CommandDispatcher>();
    }
}



public class CommandDispatcher : ICommandDispatcher
{
    private readonly IServiceProvider _serviceProvider;
    private readonly Dictionary<string, Type> _registeredCommands = new Dictionary<string, Type>();

    public CommandDispatcher(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
        _registeredCommands.Add("CreateSignalMetaData", typeof(CreateSignalMetaDataHandler));
        _registeredCommands.Add("CreatePDCQueryConfig", typeof(CreatePDCQueryConfigHandler));
    }

    public object Dispatch(string json)
    {
        JObject jObject = JObject.Parse(json);
        JToken jToken = jObject["type"];
        if (jToken == null)
        {
            throw  new Exception("The parameter type is missing in the JSON string (Pay attention: it is casse sensitive).");

        }
        Type typeCommandHandler = _registeredCommands[jToken.ToString()];
        dynamic commandHandler = ActivatorUtilities.CreateInstance(_serviceProvider, typeCommandHandler);
        dynamic o = jObject.ToObject(commandHandler.InputType);
        return commandHandler.Handle(o);
    }
}



[Route("api/[controller]")]
[ApiController]
public class ConfigurationController : ControllerBase
{
    private readonly ILog _logger;
    private readonly IConfigurationService _configurationService;
    private readonly ICommandDispatcher _commandDispatcher;

    public ConfigurationController(ICommandDispatcher commandDispatcher)
    {
        this._commandDispatcher = commandDispatcher;
    }

    // POST api/configuration
    [HttpPost]
    public IActionResult Post([FromBody] dynamic  json)
    {
        var result = _commandDispatcher.Dispatch(json.ToString());                
        return Ok(result);
    }
}

using OSPC.Configuration.Msg;
using OSPC.Configuration.Objects;
using OSPC.Shared.Core.Commands;
using OSPC.Shared.Core.Data;
using System.Collections.Generic;

namespace OSPC.Configuration.CommandHandler
{
    public class CreateSignalMetaDataHandler : CommandHandlerBase<CreateSignalMetaDataRequest, CreateSignalMetaDataResponse>
    {
        private readonly IRepository<SignalMetaData> _signalMetaDataRepository;

        public CreateSignalMetaDataHandler(IRepository<SignalMetaData> signalMetaDataRepository)
        {
            _signalMetaDataRepository = signalMetaDataRepository;
        }

        public override CreateSignalMetaDataResponse Handle()
        {            
            Output.Success = false;

            var result = new CreateSignalMetaDataResponse
            {
                SignalMetaDatas = new List<CreateSignalMetaDataResponse.SignalMetaData>()
            };

            foreach (var item in Input.Payload.SignalMetaDatas)
            {
                var signalMetaData = new Objects.SignalMetaData()
                {
                    SignalName = item.SignalName,
                    Unit = item.Unit,
                    SignalDescription = item.SignalDescription,
                    Source = item.Source
                };
                _signalMetaDataRepository.Insert(signalMetaData);

                result.SignalMetaDatas.Add(new CreateSignalMetaDataResponse.SignalMetaData()
                {
                    Id = signalMetaData.Id,
                    SignalName = signalMetaData.SignalName,
                    Unit = signalMetaData.Unit,
                    SignalDescription = signalMetaData.SignalDescription,
                    Source = signalMetaData.Source
                });
            }

            Output.Success = true;        

            return result;
        }
    }
}
aadiver
  • 26
  • 4
  • what is `ActivatorUtilities`? – Daniel A. White Feb 08 '19 at 16:24
  • What is the defination for `CreateSignalMetaDataHandler`? – Edward Feb 11 '19 at 08:07
  • @daniel-white ActivatorUtilities is a static class from Microsoft (assembly Microsoft.Extensions.DependencyInjection.Abstractions.dll). – aadiver Feb 11 '19 at 09:40
  • @daniel-white The purpose is to instantiate a new commandhandler via reflection (based on the field "type" in the json string). I want to reuse the existing IServiceCollection defined in the StartUp class and ActivatorUtilities.CreateInstance is allowing to pass an IServiceProvider. So when calling the commandhandler the dependecy injection will do its work. – aadiver Feb 11 '19 at 09:51
  • @Tao Zhou I've added CreateSignalMetaDataHandler to the code. – aadiver Feb 11 '19 at 09:54
  • Maybe it's something like this https://stackoverflow.com/a/48591356/66207 – keuleJ Feb 11 '19 at 12:16
  • Is this Exception during Startup or while dealing with a request? – keuleJ Feb 11 '19 at 12:35

2 Answers2

2

For IRepository<>, it is scoped, and for ICommandDispatcher, it is singleton. For ActivatorUtilities.CreateInstance(_serviceProvider, typeCommandHandler);, the _serviceProvider is root provider which could not resove the scoped service.

Try code below to create a new scoped provider.

dynamic commandHandler = ActivatorUtilities.CreateInstance(
    _serviceProvider.CreateScope().ServiceProvider, typeCommandHandler);
Edward
  • 28,296
  • 11
  • 76
  • 121
  • 1
    That is certainly the reason for the problem. But I'm not shure, your suggestion is the best solution – keuleJ Feb 12 '19 at 09:20
  • Won't creating a new scope just create new instances of every scoped injected type? some which might hold data you depend on in your code logic – Yahya Hussein Sep 04 '21 at 06:23
0

The solution of Tao Zhou is working but I don’t know if this solution also solves the captive dependency problem?

I’ve solved it in the following way: In the startup class I’ve changed it to AddScoped in place of AddSingleton

  services.AddScoped<ICommandDispatcher, CommandDispatcher>();

Otherwise I’ve the captive dependency problem (https://andrewlock.net/the-dangers-and-gotchas-of-using-scoped-services-when-configuring-options-in-asp-net-core/).

We can NOT define it is as Singleton (CommandDispatcher) because otherwise we have a captive dependency problem because the CommandDispatcher will eventually instantiate a CommandHandler (f.e. CreateSignalMetaDataHandler) and in the constructor one or more EFRepositories must be injected which on their turn must have a DbContext injected. The purpose is that the instantiation of DbContext must be scoped. So each HTTP request must have one and only one instance of DbContext injected. So if we are using different repositories they must all share the same DbContext. And each HTTP request has its own DbContext and a different HTTP request will have a different DbContext. Because DbContext must be scoped, the CommandDispatcher all the way up must also be scoped to avoid the captive dependency problem. So CommandDispatcher can not be defined as Singleton.

aadiver
  • 26
  • 4