【问题标题】:Dependecy Injection : Cannot resolve scoped service依赖注入:无法解析范围服务
【发布时间】:2019-07-02 17:14:12
【问题描述】:

我得到以下异常:

System.InvalidOperationException: '无法从根提供程序解析范围服务'OSPC.Shared.Core.Data.IRepository`1[OSPC.Configuration.Objects.SignalMetaData]'。' 在 CommandDispatcher 类中的 ActivatorUtilities.CreateInstance 行上。

在调查注入 CommandDispatcher 类的参数 serviceProvider 时,我可以在 ResolvedServices 集合中看到 122 个项目(所有与 Microsoft 相关的服务),但我在 ConfigureServices 方法中注册的服务都没有在集合中。

为什么我自己注册的服务没有出现?

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

【问题讨论】:

  • 什么是ActivatorUtilities
  • CreateSignalMetaDataHandler 的定义是什么?
  • @daniel-white ActivatorUtilities 是来自 Microsoft 的静态类(程序集 Microsoft.Extensions.DependencyInjection.Abstractions.dll)。
  • @daniel-white 目的是通过反射(基于json字符串中的“type”字段)实例化一个新的commandhandler。我想重用 StartUp 类中定义的现有 IServiceCollection,并且 ActivatorUtilities.CreateInstance 允许传递 IServiceProvider。因此,当调用命令处理程序时,依赖注入将完成它的工作。
  • @Tao Zhou 我已经在代码中添加了 CreateSignalMetaDataHandler。

标签: c# asp.net-core dependency-injection


【解决方案1】:

对于IRepository&lt;&gt;,它是作用域的,对于ICommandDispatcher,它是单例的。对于ActivatorUtilities.CreateInstance(_serviceProvider, typeCommandHandler);_serviceProvider 是无法解析范围服务的根提供程序。

尝试下面的代码来创建一个新的范围提供程序。

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

【讨论】:

  • 这当然是问题的原因。但我不放心,你的建议是最好的解决方案
  • 不会创建一个新的作用域只是创建每个作用域注入类型的新实例吗?一些可能包含您在代码逻辑中依赖的数据
【解决方案2】:

周涛的解决方案是可行的,但不知道这个解决方案是否也解决了强制依赖问题?

我已经通过以下方式解决了它: 在启动类中,我将其更改为 AddScoped 代替 AddSingleton

  services.AddScoped<ICommandDispatcher, CommandDispatcher>();

否则我会遇到强制依赖问题 (https://andrewlock.net/the-dangers-and-gotchas-of-using-scoped-services-when-configuring-options-in-asp-net-core/)。

我们不能将其定义为 Singleton (CommandDispatcher),否则我们会遇到强制依赖问题,因为 CommandDispatcher 最终将实例化一个 CommandHandler (fe CreateSignalMetaDataHandler),并且在构造函数中必须注入一个或多个 EFRepositories,而这些 EFRepositories 必须依次注入有一个 DbContext 注入。 目的是必须限定 DbContext 的实例化。因此,每个 HTTP 请求都必须注入一个且只有一个 DbContext 实例。因此,如果我们使用不同的存储库,它们必须都共享相同的 DbContext。而且每个 HTTP 请求都有自己的 DbContext,不同的 HTTP 请求会有不同的 DbContext。 因为 DbContext 必须有范围,所以 CommandDispatcher 一直向上也必须有范围以避免捕获依赖问题。所以CommandDispatcher不能定义为Singleton。

【讨论】:

    猜你喜欢
    • 2021-10-29
    • 1970-01-01
    • 2017-03-20
    • 2012-05-26
    • 1970-01-01
    • 1970-01-01
    • 2019-06-04
    • 2018-09-27
    • 2023-01-21
    相关资源
    最近更新 更多