Внедрение зависимостей: не удается разрешить службу с заданной областью - PullRequest
0 голосов
/ 08 февраля 2019

Я получаю следующее исключение:

System.InvalidOperationException: «Не удается разрешить службу с областью действия» OSPC.Shared.Core.Data.IRepository`1 [OSPC.Configuration.Objects.SignalMetaData] 'изкорневой провайдер. 'в строке ActivatorUtilities.CreateInstance в классе CommandDispatcher.

При изучении параметра serviceProvider, который внедряется в класс CommandDispatcher, я вижу 122 элемента в коллекции ResolvedServices (все связанные с 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;
        }
    }
}

Ответы [ 2 ]

0 голосов
/ 12 февраля 2019

Решение Дао Чжоу работает, но я не знаю, решает ли это решение и проблему зависимостей в неволе?

Я решил ее следующим образом: В классе запуска я изменилэто 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 (например, CreateSignalMetaDataHandler), а в конструкторе должен быть введен один или несколько объектов EFRepositories, которым, в свою очередь, должен быть вставлен DbContext. Цель состоит в том, чтобы экземпляр DbContext был ограничен.HTTP-запрос должен содержать один и только один экземпляр DbContext. Поэтому, если мы используем разные репозитории, все они должны использовать один и тот же DbContext. И каждый HTTP-запрос имеет свой собственный DbContext, а другой HTTP-запрос будет иметь другой DbContex.т.Поскольку область действия DbContext должна быть ограничена, область действия CommandDispatcher также должна быть ограничена, чтобы избежать проблемы зависимостей в неволе.Поэтому CommandDispatcher не может быть определен как Singleton.

0 голосов
/ 12 февраля 2019

Для IRepository<> это область действия, а для ICommandDispatcher это синглтон.Для ActivatorUtilities.CreateInstance(_serviceProvider, typeCommandHandler); _serviceProvider является корневым провайдером, который не смог восстановить сервис с областью действия.

Попробуйте код ниже, чтобы создать нового поставщика области.

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