简体   繁体   English

依赖注入:无法解析范围服务

[英]Dependecy Injection : Cannot resolve scoped service

I get the following exception: 我得到以下异常:

System.InvalidOperationException: 'Cannot resolve scoped service 'OSPC.Shared.Core.Data.IRepository`1[OSPC.Configuration.Objects.SignalMetaData]' from root provider.' 'System.InvalidOperationException:'无法从根提供者解析作用域服务'OSPC.Shared.Core.Data.IRepository`1 [OSPC.Configuration.Objects.SignalMetaData]'。 on the line ActivatorUtilities.CreateInstance in the CommandDispatcher class. 在CommandDispatcher类中的ActivatorUtilities.CreateInstance行上。

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. 在调查在CommandDispatcher类中注入的参数serviceProvider时,我可以在ResolvedServices集合(所有与Microsoft相关的服务)中看到122个项目,但是在ConfigureServices方法中注册的我的服务都不在该集合中。

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;
        }
    }
}

For IRepository<> , it is scoped, and for ICommandDispatcher , it is singleton. 对于IRepository<> ,它是作用域的;对于ICommandDispatcher ,它是单例的。 For ActivatorUtilities.CreateInstance(_serviceProvider, typeCommandHandler); 对于ActivatorUtilities.CreateInstance(_serviceProvider, typeCommandHandler); , the _serviceProvider is root provider which could not resove the scoped service. _serviceProvider是无法解析作用域服务的根提供者。

Try code below to create a new scoped provider. 尝试下面的代码创建一个新的作用域提供程序。

dynamic commandHandler = ActivatorUtilities.CreateInstance(
    _serviceProvider.CreateScope().ServiceProvider, typeCommandHandler);

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 我已通过以下方式解决了该问题:在启动类中,我已将其更改为AddScoped而不是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/ ). 否则,我会遇到专属依赖性问题( 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 (fe CreateSignalMetaDataHandler) and in the constructor one or more EFRepositories must be injected which on their turn must have a DbContext injected. 我们不能将其定义为Singleton(CommandDispatcher),因为否则,我们会遇到圈养依赖性问题,因为CommandDispatcher最终将实例化CommandHandler(fe CreateSignalMetaDataHandler),并且在构造函数中必须注入一个或多个EFRepository,而它们又必须具有DbContext注射。 The purpose is that the instantiation of DbContext must be scoped. 目的是必须限制DbContext的实例化。 So each HTTP request must have one and only one instance of DbContext injected. 因此,每个HTTP请求必须注入一个DbContext实例,并且只有一个实例被注入。 So if we are using different repositories they must all share the same DbContext. 因此,如果我们使用不同的存储库,那么它们都必须共享相同的DbContext。 And each HTTP request has its own DbContext and a different HTTP request will have a different DbContext. 每个HTTP请求都有其自己的DbContext,并且不同的HTTP请求将具有不同的DbContext。 Because DbContext must be scoped, the CommandDispatcher all the way up must also be scoped to avoid the captive dependency problem. 因为必须限制DbContext的范围,所以也必须一直限制CommandDispatcher的范围,以避免俘虏依赖性问题。 So CommandDispatcher can not be defined as Singleton. 因此CommandDispatcher不能定义为Singleton。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM