Home > Mobile >  Background Service using IServiceScopeFactory to get a scoped service Not Working
Background Service using IServiceScopeFactory to get a scoped service Not Working

Time:08-04

I am implementing a BackgroundService that will perform calling a function within IJobService. I have actually used ServiceScopeFactory to inject this service inside the BackgroundService. I am still getting this error "Cannot access a disposed context instance. A common cause of this error is disposing a context instance that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling 'Dispose' on the context instance, or wrapping it in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.\r\nObject name: 'DBContext'."


 public class BackgroundSubscriber : BackgroundService
{
    /// <summary>
    /// 
    /// </summary>
    public IServiceScopeFactory _serviceScopeFactory;
    /// <summary>
    /// 
    /// </summary>
    private readonly IConnectionMultiplexer _connectionMultiplexer;
   /// <summary>
   /// 
   /// </summary>
   /// <param name="serviceScopeFactory"></param>
   /// <param name="connectionMultiplexer"></param>
    public CustomAlertRedisSubscriber(IServiceScopeFactory serviceScopeFactory, IConnectionMultiplexer connectionMultiplexer)
    {
        _serviceScopeFactory = serviceScopeFactory;
        _connectionMultiplexer = connectionMultiplexer;
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="stoppingToken"></param>
    /// <returns></returns>
    protected override Task ExecuteAsync(CancellationToken stoppingToken)
    {
        using (var scope = _serviceScopeFactory.CreateScope())
        {
            var scoped = scope.ServiceProvider.GetRequiredService<IJobService>();
            var sub = _connectionMultiplexer.GetSubscriber();

            return sub.SubscribeAsync($"Job", (channel, value) =>
            {
                var watcher = System.Text.Json.JsonSerializer.Deserialize<Model> 
     (value.ToString());
                scoped.BackgrounJobAsync(watcher);
            });
        }

    }
  }
}

public class JobService : IJobService
{
    private readonly IUnitOfWork _unitOfWork;

    public JobService(
        IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }

  public async Task BackgrounJobAsync(Model model)
    {
            var list = await _unitOfWork.JobRepository.GetAllByIdAsync(model.id);
    }
 }

public interface IUnitOfWork : IDisposable
{
    IJobRepository JobRepository { get; }
}

 public class UnitOfWork : IUnitOfWork
 {
    private readonly TrackerDBContext _dbContext;
    private readonly AppSettings _appSettings;

    public UnitOfWork(TrackerDBContext dbContext, AppSettings appSettings)
    {
        _dbContext = dbContext;
        _appSettings = appSettings;
    }

    private IJobRepository _jobRepository;
    public IJobRepository JobRepository => _jobRepository= _jobRepository?? new 
    JobRepository(_dbContext);


    public void Dispose()
    {
        GC.SuppressFinalize(this);
    }
}

 public class JobRepository: IJobRepository
 {
    private readonly TrackerDBContext _dbContext;
    public InventoryCustomAlertsWatcherRepository(TrackerDBContext dbContext)
    {
        _dbContext = dbContext;
    }


    public async Task<Modle> GetAllByIdAsync(Modle modle)
    {
        return await _dbContext.Job.Where(x => modle.Id).ToListAsync();
    }

 }

CodePudding user response:

You need to make your method async. The problem is now you are returning a Task and your method is inside a using statement. So it will dispose the scope when it is returning the Task. You need to await the SubscribeAsync.

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    using (var scope = _serviceScopeFactory.CreateScope())
    {
        var scoped = scope.ServiceProvider.GetRequiredService<IJobService>();
        var sub = _connectionMultiplexer.GetSubscriber();

        await sub.SubscribeAsync($"Job", (channel, value) =>
        {
            var watcher = System.Text.Json.JsonSerializer.Deserialize<Model> 
     (value.ToString());
            scoped.BackgrounJobAsync(watcher);
        });
    }
}

CodePudding user response:

In addition to making ExecuteAsync an async method, you also need to make the lambda passed to SubscribeAsync async. In fact, you should just go through all your code and anywhere you see *Async being called, ensure there's an await to match.

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    using (var scope = _serviceScopeFactory.CreateScope())
    {
        var scoped = scope.ServiceProvider.GetRequiredService<IJobService>();
        var sub = _connectionMultiplexer.GetSubscriber();

        await sub.SubscribeAsync($"Job", async (channel, value) =>
        {
            var watcher = System.Text.Json.JsonSerializer.Deserialize<Model> 
     (value.ToString());
            await scoped.BackgrounJobAsync(watcher);
        });
    }
}
  • Related