#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 и пытаетесь использовать его после того, как он уже удален.
Я могу предложить несколько подходов, которые вы можете рассмотреть, и выбрать тот, который подходит вам лучше всего.
- Сделайте посредника одноэлементным сервисом
- Не кэшируйте LotteryService, и make также является сервисом с ограниченной областью действия
- Я не знаю, зачем LotteryService нужен IServiceProvider, но, возможно, вы можете передать его зависимость при вызове метода