не удается получить доступ к уже удаленному объекту ServiceProvider

#c# #asp.net-core #dependency-injection #factory-pattern #mediator

#c# #asp.net-core #внедрение зависимостей #фабричный шаблон #посредник

Вопрос:

У меня есть класс-посредник, подобный этому, где я инициализирую словарь с помощью множества сервисов:

 public class Mediator : IMediator
    {
        static readonly ConcurrentDictionary<int, Func<IGameBaseService>> services = new ConcurrentDictionary<int, Func<IGameBaseService>>();
        protected readonly IServiceProvider _serviceProvider;

        public Mediator(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
        }
        public async Task<Func<IGameBaseService>> GetGameServiceAsync(int gameId)
        {
            if (services.Count == 0)
                await AddServices();
            return await Task.FromResult(services[gameId]);
        }

        private async Task AddServices()
        {
            await Task.FromResult(services.GetOrAdd((int)Game.Blackjack, () => new LotteryService(_serviceProvider)));
        }
  

Я вызываю GetStateAsync следующим образом, чтобы получить состояние игры:

 public async Task<ResponseBase> GetStateAsync(ApiGetStateInput input)
{
    var gameService = await _mediator.GetGameServiceAsync(input.Identity.GameId);
    var state = gameService().GetStateAsync(input.Identity.GameTableId);
    response.ResponseObject = state.Dump();
}
  

Вызов проходит через посредника, чтобы узнать, какую игровую службу вызывать. он создает экземпляр службы, передавая serviceprovidor через конструктор. служба лотерейных игр выглядит примерно так:

 public class LotteryService : BaseService, IGameBaseService
    {
       public LotteryService (IServiceProvider serviceProvider) : base(serviceProvider)
        {
            
        }
    }
public Task<IEnumerable<BlGameState>> GetStateAsync(int tableId)
        {
            //logic
            return something;
        }
  
  public class BaseService : IBaseService
    {
        protected readonly IServiceProvider _serviceProvider;
        protected readonly IUnitOfWork _unitOfWork;
        protected readonly ICacheProvider _cache;
        protected readonly IMapper _mapper;

        public BaseService(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
            _mapper = _serviceProvider.GetService<IMapper>();
            _unitOfWork = _serviceProvider.GetService<IUnitOfWork>();
            _cache = _serviceProvider.GetService<ICacheProvider>();
        }
}
  

И я зарегистрировал все это в конфигурационных службах запуска:

             services.AddAutoMapper(typeof(Startup));
            
            services.AddScoped<IUnitOfWork, UnitOfWork>();
            services.AddScoped<ICacheProvider, CacheProvider>();
            services.AddScoped<IMediator, Mediator>();
  

Проблема в том, что когда я запускаю проект и вызываю GetStateAsync, он работает отлично. Но после повторного вызова похоже, что ServiceProvider удаляется и не может получить экземпляр ServiceProvider. Как решить эту проблему и есть ли способ улучшить этот код и архитектуру получения сервисов?
Вот ошибка:

 Cannot access a disposed object.rnObject name: 'IServiceProvider'._   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ThrowHelper.ThrowObjectDisposedException()rn   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)rn   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider provider)rn
  

Комментарии:

1. Вы можете попробовать выполнить инъекцию IServiceScope и вызвать _serviceScope.ServiceProvider.GetService<TService> . Не забудьте удалить его с _serviceScope.Dispose() помощью после того, как вы закончите с ним.

2. @N.Dogac теперь он не запущен. говорят, что невозможно внедрить IServiceScope

Ответ №1:

Проблема в том, что ваш посредник является сервисом с ограниченной областью действия (что означает, что новый создается для каждого запроса, а затем удаляется) из-за того, что экземпляр IServiceProvider, который он получает, также действителен в этой области, но вы сохраняете его в LotteryService и пытаетесь использовать его после того, как он уже удален.

Я могу предложить несколько подходов, которые вы можете рассмотреть, и выбрать тот, который подходит вам лучше всего.

  1. Сделайте посредника одноэлементным сервисом
  2. Не кэшируйте LotteryService, и make также является сервисом с ограниченной областью действия
  3. Я не знаю, зачем LotteryService нужен IServiceProvider, но, возможно, вы можете передать его зависимость при вызове метода