Open Closed

Hangfire background job : Cannot access a disposed context instance #3985


User avatar
0
hussein created

Check the docs before asking a question: https://docs.abp.io/en/commercial/latest/ Check the samples, to see the basic tasks: https://docs.abp.io/en/commercial/latest/samples/index The exact solution to your question may have been answered before, please use the search on the homepage.

If you're creating a bug/problem report, please include followings:

  • ABP Framework version: v6.0.1
  • UI type: MVC
  • DB provider: EF Core / MongoDB
  • Tiered (MVC) or Identity Server Separated (Angular): no
  • Exception message and stack trace:
  • Steps to reproduce the issue:"

my worker as the following:

 public interface IBillingWorker : IHangfireBackgroundWorker
 {
 }
 
 [ExposeServices(typeof(IBillingWorker))]
 public class BillingWorker : HangfireBackgroundWorkerBase, IBillingWorker
 {
     private readonly ILogger _logger;
     private readonly IBillingBatchRepository _billingBatchRepository;
     public BillingWorker(ILogger logger, IBillingBatchRepository billingBatchRepository)
     {
         RecurringJobId = nameof(BillingWorker);
         CronExpression = Cron.Minutely();
         _logger = logger;
         _billingBatchRepository = billingBatchRepository;
         }
         
         [UnitOfWork(isTransactional: false)]
         public override async Task DoWorkAsync(CancellationToken cancellationToken = default)
         {
             _logger.LogInformation("start billingWorker..!");
             var querable = await _billingBatchRepository.GetQueryableAsync();
             var query = querable.Where(q = q.EndTime == default && q.StartTime == default
             && q.ExecutionTime <= DateTime.Now
             ).Select(x = x.Id).ToList();
             _logger.LogInformation($"Executed billingWorker..!: return {query.Count}");
             await Task.CompletedTask;
         }
    }

and i registred it as the following:

 public override void OnApplicationInitialization(ApplicationInitializationContext context)
 {
     AsyncHelper.RunSync(async () =
     {
        await context.AddBackgroundWorkerAsync();
     });
}



[DependsOn(typeof(AbpBackgroundJobsModule))]
[DependsOn(typeof(AbpBackgroundJobsHangfireModule))]
[DependsOn(typeof(AbpBackgroundWorkersHangfireModule))]
public class BstUtilityBackgroundServicesModule : AbpModule
{
...
}

once the worker is started it gives the following 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: 'BillingDbContext'.

if i use the following code

using (var uow = _unitOfWorkManager.Begin())
{
 var querable = await _billingBatchRepository.GetQueryableAsync();
 var query = querable.Where(q => q.EndTime == default && q.StartTime == default
 && q.ExecutionTime<=DateTime.Now
 ).Select(x => x.Id);
 list = await AsyncExecuter.ToListAsync(query);
}

it keeps returning an empty list!! , but if I call it manually from the appservice , then it will return the data

Note that I BillingDbContext is in another module

i'll appreciate if you can provide a working sample with best practices on how we can use hangfire with ABP ,,,, noting that I read the documents files but had no luck because if I use it in appservice, not in the domain it will give an authorization field even if I use [AllowAnonymous]


3 Answer(s)
  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Hi,

    Can you provide a minimal project that can reproduce the problem with me? thanks. shiwei.liang@volosoft.com

  • User Avatar
    0
    hussein created

    hello liangshiwei, the job calls a function in the domain layer, I realized that no filter no tenant no user logged :) so i updated the function to disable all the filters and add the unit of work inside the method 1- I don't know if this is a good practice or not, especially since it may have about 4M records! your advice plz

    2- kindly could you guide me on how I use it with application service layer?

    i modified my code as the following , maybe it will help the others :)

    public async Task CalcInvoicesForAllBatches(bool fromJob=false)
    {
        List<Guid> TenantList = new List<Guid>();
        if (fromJob)
        {
            TenantList = (await _tenantRepository.GetListAsync()).Select(x=>x.Id).ToList();
         }
         else
         {
             TenantList.Add( _currentTenant.GetId());
         }
         if (fromJob) using (_dataFilter.Disable<ICompanyFilter>())
         if (fromJob) using (_dataFilter.Disable<IBranchFilter>())
         if (fromJob) using (_dataFilter.Disable<IOfficeFilter>())
         if (fromJob) using (_dataFilter.Disable<IRegistryFilter>())
         //if (fromJob) using (_dataFilter.Disable<IMultiTenant>()) didnt work, it will include host =null
         foreach (var tenant in TenantList)
         using (_currentTenant.Change(tenant))
         using (var uow = _unitOfWorkManager.Begin())
         {
             var querable = await _billingBatchRepository.GetQueryableAsync();
             var query = querable.Where(q => q.EndTime == default && q.StartTime == default
             && q.ExecutionTime <= DateTime.Now
             ).Select(x => x.Id);
             var  list = await AsyncExecuter.ToListAsync(query);
             foreach (var id in list)
             {
             await CalcInvoicesByBatchId(id);
             }
         }
     }
    

    3- how i can localize and translate the background dashboard? 4- is it ok to keep the default workers as the following output? if i want to minimize it how i can configure it to increase running time intervals

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    1- I don't know if this is a good practice or not, especially since it may have about 4M records! your advice plz

    I don't about your user case, If it doesn't affect your business then it's okay.

    2- kindly could you guide me on how I use it with application service layer?

    We do not recommend that you use App Services in a background worker.

    3- how i can localize and translate the background dashboard?

    It's not related to ABP, some discussion here: https://github.com/HangfireIO/Hangfire/issues/881 I think hangfire supports localization, you can send pr to hangfire if there is no your culture like:https://github.com/HangfireIO/Hangfire/pull/1443

    >4- is it ok to keep the default workers as the following output? if i want to minimize it how i can configure it to increase running time intervals Yes, you can.

    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        Configure<AbpBackgroundJobWorkerOptions>(options =>
        {
            options.JobPollPeriod = 10000; //10 seconds
        });
        
        Configure<TokenCleanupOptions>(options=>
        {
            options.CleanupPeriod = xxxxx;
        });
    }
    
Made with ❤️ on ABP v8.2.0-preview Updated on March 25, 2024, 15:11