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 });
3 Answer(s)
-
0
- 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.idis alwaysnullinside 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
- Pass
tenantIdwhen enqueueing (you already do this):
var jobId = await jobs.enqueueAsync('SendCustomerSummary', { customerId: body.customerId, to: 'marco.susani@nicma.com', tenantId: currentTenant.id });- 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.Changeis 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
TenantIdis set fromICurrentTenant.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) - In ABP, background jobs don’t run in an authenticated HTTP request, so there is no bearer token → no resolved tenant. That’s why
-
0
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) -
0
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.idis null while the job script runs, anddb.get('Customer', ...)only looks at the host data. ThetenantIdyou put into the job data is just part of your payload, the framework doesn't read it.Keep passing
tenantIdas 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)