Search code examples
c#asp.net-corehangfireabp-framework

abp.io Hangfire Cannot access a disposed context instance


I am trying to set up a Hangfire recurrent job on application startup, but if I try to use an injected IRepository inside the job; it gives me this error

System.ObjectDisposedException
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. Object name: 'TMSDbContext'.

System.ObjectDisposedException: 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.
Object name: 'TMSDbContext'.
   at Microsoft.EntityFrameworkCore.DbContext.CheckDisposed()
   at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
   at Microsoft.EntityFrameworkCore.DbContext.get_Model()
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.get_EntityType()
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.CheckState()
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.get_EntityQueryable()
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.System.Linq.IQueryable.get_Provider()
   at Volo.Abp.EntityFrameworkCore.EfCoreAsyncQueryableProvider.CanExecute[T](IQueryable`1 queryable)
   at Volo.Abp.Linq.AsyncQueryableExecuter.<>c__DisplayClass4_0`1.<FindProvider>b__0(IAsyncQueryableProvider p)
   at System.Linq.Enumerable.TryGetFirst[TSource](IEnumerable`1 source, Func`2 predicate, Boolean& found)
   at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source, Func`2 predicate)
   at Volo.Abp.Linq.AsyncQueryableExecuter.FindProvider[T](IQueryable`1 queryable)
   at Volo.Abp.Linq.AsyncQueryableExecuter.ToListAsync[T](IQueryable`1 queryable, CancellationToken cancellationToken)
   at Volo.Abp.Domain.Repositories.RepositoryAsyncExtensions.ToListAsync[T](IReadOnlyRepository`1 repository, CancellationToken cancellationToken)
   at MB.TMS.Application.BackgroundServices.Hangfire.BookingAlarmFunction() in C:\Users\ahmad\source\repos\TMS-Abp4\aspnet-core\src\MB.TMS.Application\BackgroundServices\Hangfire.cs:line 53
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()

I've registered Hangfire exactly as it is described in the ABP documentation, And its working on basic recurring job like

RecurringJob.AddOrUpdate(
    () => Console.WriteLine("Recurring!"),
    Cron.Daily);

I've registered my service as Scoped service in ApplicationModule

 public override void ConfigureServices(ServiceConfigurationContext context)
        {
            Configure<AbpAutoMapperOptions>(options =>
            {
                options.AddMaps<TMSApplicationModule>();
            });


            context.Services.AddScoped<IHangfire, Hangfire>();
        }

And my job method is simply trying to do a query with an injected repository.

    public class Hangfire : IHangfire
    {
    
        private readonly IRepository<Booking, int> _bookingsRepository;
        private readonly IBookingSignalR _bookingSignalR;
        private readonly IObjectMapper _objectMapper;

        public Hangfire(
            IRepository<Booking, int> bookingsRepository,
            IBookingSignalR bookingSignalR,
            IObjectMapper objectMapper)
        {
            _bookingsRepository = bookingsRepository;
            _bookingSignalR = bookingSignalR;
            _objectMapper = objectMapper;
        }
    
        public void BookingAlarmRecurringJob()
        {
        RecurringJob.AddOrUpdate(
        () => BookingAlarmFunction(),
        Cron.Minutely());
        }

        [UnitOfWork]
        public virtual async Task BookingAlarmFunction()
        {
            

            var bookings = await _bookingsRepository
                                        .ToListAsync();

            bookings.ForEach(async booking =>
            {
                if (IsPlanningAlramActive(booking))
                {
                    booking.IsPlanningAlarmActive = true;
                    await SendSignalR(booking);

                }

                else if (IsPickupAlarmActive(booking))
                {
                    booking.IsPlanningAlarmActive = false;
                    booking.IsPickupAlarmActive = true;
                    await SendSignalR(booking);
                }
            });

        }
    }
}

Solution

  • creating a uow in BookingAlarmFunction method manually did the trick

    public class Hangfire : IHangfire
        {
            private readonly IUnitOfWorkManager _unitOfWorkManager;
    
            public Hangfire(
                IUnitOfWorkManager unitOfWorkManager)
            {
                _unitOfWorkManager = unitOfWorkManager;
            }
    
            public virtual async Task BookingAlarmFunction()
            {
                using (var uow = _unitOfWorkManager.Begin(requiresNew: true, isTransactional: true))
                {
                    var bookings = await _bookingsRepository
                                           .ToListAsync();
    
                    await uow.CompleteAsync();
                }
    
            }
    }