Open Closed

Abp Low Level Code background job #10877


User avatar
0
marco.susani created

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

Provide us with the following info: 🧐 Hint: If you are using the ABP Studio, you can see all the information about your solution from the configuration window, which opens when you right-click on the solution and click on the Solution Configuration button.

  • Exception message and full stack trace:
  • Steps to reproduce the issue:
  • Template: app-nolayers
  • Template Type: Modern
  • Created ABP Studio Version: 3.0.7
  • Current ABP Studio Version: 3.0.10
  • Multi-Tenancy: Yes
  • UI Framework: react
  • Theme: leptonx
  • Theme Style: system
  • Database Provider: ef
  • Database Management System: sqlserver
  • Public Website: Yes
  • Social Login: No
  • Include Tests: Yes
  • Use Local References: No
  • Optional Modules:
    • GDPR
    • TextTemplateManagement
    • AuditLogging
    • OpenIddictAdmin
    • LowCode
  • Selected Languages: Italian, English
  • AI Management Enabled: Yes
  • Selected AI Providers: OpenAI
  • Default Language: Italian
  • Create Command: abp new AbpSolution4 -t app-nolayers --ui-framework react --database-provider ef --database-management-system sqlserver --theme leptonx --skip-migration --skip-migrator --public-website --without-cms-kit --sample-crud-page --dont-run-install-libs --dont-run-bundling --no-social-logins -no-file-management -no-language-management -low-code

Hi, i'm trying to create a background job in low level code design, but there isn't a way to fetch record from a dynamic entity filtered by tenant id. This is my Scripting API javascript for background job:

log("current tenant: " + currentTenant.id); var customer = await db.get('Customer', jobData.customerId); if (!customer) { userFriendlyError('Customer not found.'); } await email.queueAsync(jobData.to, 'Customer summary', customer.RagioneSociale);

Current tenant is always null. This is how i enqueue the job from a custom endpoint (always created from low level code), here currentTenant.id is correct (because the framework fetch it from bearer token with tenant specification):

var jobId = await jobs.enqueueAsync('SendCustomerSummary', { customerId: body.customerId, to: 'marco.susani@nicma.com', tenantId : currentTenant.id });

Markdown supported.
Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)

3 Answer(s)
  • User Avatar
    0
    AI-Bot created
    Support Team ABP Agent
    • In ABP, background jobs don’t run in an authenticated HTTP request, so there is no bearer token → no resolved tenant. That’s why currentTenant.id is always null inside the LowCode background job script by default.
    • To fetch tenant-specific data from a dynamic entity in a background job, you must explicitly switch the tenant context at the beginning of the job execution, using the tenant id you pass in jobData.

    What to do

    1. Pass tenantId when enqueueing (you already do this):
    var jobId = await jobs.enqueueAsync('SendCustomerSummary', {
      customerId: body.customerId,
      to: 'marco.susani@nicma.com',
      tenantId: currentTenant.id
    });
    
    1. In the background job script, explicitly change the tenant context before calling db.get:

    Conceptually you need the equivalent of ABP’s server-side approach:

    • using (_currentTenant.Change(tenantId)) { ... }

    So your job should be structured like:

    // PSEUDO (depends on the exact LowCode scripting API functions available in your version)
    if (!jobData.tenantId) {
      userFriendlyError("tenantId is required for this job.");
    }
    
    // switch tenant context first
    await currentTenant.changeAsync(jobData.tenantId);  // or currentTenant.change(...)/withTenant(...)
    
    // now tenant filter / connection selection will work as expected
    var customer = await db.get('Customer', jobData.customerId);
    if (!customer) { userFriendlyError('Customer not found.'); }
    
    await email.queueAsync(jobData.to, 'Customer summary', customer.RagioneSociale);
    

    If your LowCode scripting API doesn’t expose a currentTenant.change* (or similar) method, then there is no supported way to have ABP automatically apply tenant context inside that LowCode background job, and you should implement the job in “regular” ABP code (C#) where you can do:

    using (_currentTenant.Change(tenantId))
    {
        // query repositories / DbContext here
    }
    

    That is the officially recommended mechanism for tenant-specific work in background workers/jobs.

    References (verified)

    • https://abp.io/support/questions/2007 (explains ICurrentTenant.Change is the way to operate on a specific tenant in a background worker/job)
    • https://abp.io/docs/latest/framework/architecture/multi-tenancy (ABP multi-tenancy fundamentals; tenant context is normally resolved from request/environment)
    • https://abp.io/docs/10.6/release-info/migration-guides/abp-5-0 (mentions TenantId is set from ICurrentTenant.Id, reinforcing that tenant context must be set for tenant-aware operations)

    This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    marco.susani created

    There isn't the method: await currentTenant.changeAsync(jobData.tenantId);

    only readonly properties (Id, Name, isAvailable).

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    Low-Code background jobs go through ABP's dynamic background job pipeline, and that pipeline doesn't carry the tenant of the request that enqueued the job. So currentTenant.id is null while the job script runs, and db.get('Customer', ...) only looks at the host data. The tenantId you put into the job data is just part of your payload, the framework doesn't read it.

    Keep passing tenantId as you already do, and add this class to your project:

    using System;
    using System.Text.Json;
    using Microsoft.Extensions.Options;
    using Volo.Abp.BackgroundJobs;
    using Volo.Abp.DependencyInjection;
    using Volo.Abp.MultiTenancy;
    
    namespace AbpSolution4;
    
    [Dependency(ReplaceServices = true)]
    [ExposeServices(typeof(IBackgroundJobExecuter))]
    public class TenantAwareBackgroundJobExecuter : BackgroundJobExecuter
    {
        public TenantAwareBackgroundJobExecuter(
            IOptions<AbpBackgroundJobOptions> options,
            ICurrentTenant currentTenant)
            : base(options, currentTenant)
        {
        }
    
        protected override Guid? GetJobArgsTenantId(object jobArgs)
        {
            if (jobArgs is DynamicBackgroundJobArgs dynamicJobArgs && !dynamicJobArgs.JsonData.IsNullOrWhiteSpace())
            {
                using var document = JsonDocument.Parse(dynamicJobArgs.JsonData);
                if (document.RootElement.ValueKind == JsonValueKind.Object)
                {
                    foreach (var property in document.RootElement.EnumerateObject())
                    {
                        if (property.NameEquals("tenantId") &&
                            property.Value.ValueKind == JsonValueKind.String &&
                            Guid.TryParse(property.Value.GetString(), out var tenantId))
                        {
                            return tenantId;
                        }
                    }
                }
            }
    
            return base.GetJobArgsTenantId(jobArgs);
        }
    }
    

    Your job script then runs under that tenant and needs no change.

    We'll fix this on the framework side so the enqueueing tenant is carried automatically and you won't need the class above. It will be in an upcoming release. Your ticket has been refunded.

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.8.0-preview. Updated on September 28, 2026, 11:44
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.