Activities of "maliming"

Hi,

Glad that cleared it up.

The built-in background job store is host-only by design, and there's no committed plan or release target for a tenant-specific / database-per-tenant store right now. If that changes it'll show up in the release notes.

For now, the custom per-tenant worker over your tenant-aware DbContext is the way to go.

Thanks

You're welcome. Whenever you're ready, just share your project structure (version, template, single-project vs separated) and whether you still need a browser login, and I'll send you the exact module/package changes for your project.

Thanks

Hi,

This is by design, and it's actually two things stacking up, which is why none of your five attempts moved the needle:

  1. The background jobs DbContext is marked [IgnoreMultiTenancy], and BackgroundJobRecord isn't a multi-tenant entity (no TenantId). The module is meant to live in the host database.
  2. On top of that, ABP's EF Core repository treats any non-multi-tenant entity as host data. When it resolves the DbContext, it temporarily switches ICurrentTenant to null for entities that aren't IMultiTenant. BackgroundJobRecord isn't, so the connection string is pinned to host regardless of what tenant you switched to.

Because of point 2, ReplaceDbContext can't help — even if you point the store at a tenant-aware context, the repository still forces the tenant to null for this entity type. And the connection-string mapping / named-connection attempts never get consulted, since the tenant is already null by the time resolution runs.

To answer your last question: yes, for DB-per-tenant job queues you route this through a tenant-aware DbContext and bypass the host-pinning. The cleanest way is to replace IBackgroundJobRepository with one bound to your AppDbContext (which already resolves the tenant connection and has the table mapped), and override GetDbContextAsync so it stops resetting the tenant:

[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IBackgroundJobRepository))]
public class TenantAwareBackgroundJobRepository
    : EfCoreRepository<AppDbContext, BackgroundJobRecord, Guid>, IBackgroundJobRepository
{
    private readonly IDbContextProvider<AppDbContext> _dbContextProvider;

    protected IClock Clock { get; }

    public TenantAwareBackgroundJobRepository(
        IDbContextProvider<AppDbContext> dbContextProvider,
        IClock clock)
        : base(dbContextProvider)
    {
        _dbContextProvider = dbContextProvider;
        Clock = clock;
    }

    // BackgroundJobRecord is not IMultiTenant, so the base repository would switch the
    // current tenant to null and read from the host DB. Keep the current tenant instead
    // so the connection string follows it.
    protected override Task<AppDbContext> GetDbContextAsync()
    {
        return _dbContextProvider.GetDbContextAsync();
    }

    public virtual async Task<List<BackgroundJobRecord>> GetWaitingListAsync(
        string applicationName,
        int maxResultCount,
        CancellationToken cancellationToken = default)
    {
        var now = Clock.Now;
        return await (await GetDbSetAsync())
            .Where(t => t.ApplicationName == applicationName)
            .Where(t => !t.IsAbandoned && t.NextTryTime <= now)
            .OrderByDescending(t => t.Priority)
            .ThenBy(t => t.TryCount)
            .ThenBy(t => t.NextTryTime)
            .Take(maxResultCount)
            .ToListAsync(GetCancellationToken(cancellationToken));
    }
}

The built-in BackgroundJobStore just delegates to IBackgroundJobRepository, so once this replacement is in place, IBackgroundJobStore.GetWaitingJobsAsync reads from the tenant DB when you switch tenant in your worker. No new DbContext and no extra migration — you're reusing the per-tenant table you already have.

One thing to keep in mind: this makes the whole store tenant-aware, so enqueue/update/delete follow the current tenant too. Jobs added while a tenant is current get written to that tenant's DB, which is what you want for per-tenant queues. The built-in BackgroundJobWorker still runs as host and only picks up host jobs, so polling each tenant DB stays the job of your own worker that switches ICurrentTenant — which is exactly what you're already doing.

Thanks

Hi,

This is by design, not something you can toggle. The background jobs store is host-only.

IBackgroundJobsDbContext is marked with [IgnoreMultiTenancy], and when the connection string is resolved for such a context, the framework forces CurrentTenant.Change(null) before resolving:

// UnitOfWorkDbContextProvider.ResolveConnectionStringAsync
if (typeof(TDbContext).IsDefined(typeof(IgnoreMultiTenancyAttribute), false))
{
    using (CurrentTenant.Change(null)) // always host
    {
        return await ConnectionStringResolver.ResolveAsync(connectionStringName);
    }
}

EfCoreBackgroundJobRepository is bound to IBackgroundJobsDbContext, so TDbContext here is that interface and it always carries [IgnoreMultiTenancy]. That's why GetWaitingJobsAsync always hits the host DB even though your own AppDbContext resolves to the tenant DB in the same scope.

That's also why none of your five attempts changed anything — the connection string is resolved under the host tenant, before anything tenant-specific is looked at:

  • ReplaceDbContext does swap in your concrete DbContext, but the [IgnoreMultiTenancy] check runs against the repository's generic type IBackgroundJobsDbContext, not against your replacement, so the connection string is still resolved as host.
  • The AbpDbConnectionOptions remapping and the named AbpBackgroundJobs connection string on the tenant are never consulted, because the current tenant is already null at resolve time.
  • BackgroundJobRecord isn't IMultiTenant (there's no TenantId column), so the IDataFilter suppression has nothing to disable — you just got host rows back.
  • A fresh unit of work doesn't help either; the host-forcing happens inside the DbContext provider regardless of the UoW.

So there's no supported way to make IBackgroundJobStore / EfCoreBackgroundJobStore resolve to the tenant database. For a DB-per-tenant setup where the jobs physically live in each tenant DB, your second option is the right one: query BackgroundJobRecord directly through your own tenant-aware DbContext (the one that already resolves correctly):

using (_currentTenant.Change(tenantId))
{
    using var scope = serviceProvider.CreateScope();
    using var uow = scope.ServiceProvider
        .GetRequiredService<IUnitOfWorkManager>()
        .Begin(requiresNew: true, isTransactional: false);

    var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();

    var now = Clock.Now;
    var waitingJobs = await dbContext.BackgroundJobs
        .Where(j => !j.IsAbandoned && j.NextTryTime <= now)
        .OrderByDescending(j => j.Priority)
        .ThenBy(j => j.TryCount)
        .ThenBy(j => j.NextTryTime)
        .Take(100)
        .ToListAsync();

    // process, then update TryCount / NextTryTime / IsAbandoned
    // through the same dbContext

    await uow.CompleteAsync();
}

Keep the filter in sync with your ABP version's EfCoreBackgroundJobRepository.GetWaitingListAsync so the semantics match — depending on the version it may also filter on ApplicationName and CompletionTime, and those columns may not exist on older ones, so mirror exactly what your version has.

One thing to keep in mind for the whole pipeline: enqueue and update go through the same host-only store, so IBackgroundJobManager / IBackgroundJobStore write to the host DB too. If your jobs need to live in the tenant DB, insert and update them through this tenant-aware DbContext as well, not through the standard manager. And the built-in BackgroundJobWorker only polls the host store, so your own per-tenant worker is exactly the pattern to use here.

Thanks

Hi,

Yes, this is doable. The clean ABP setup for it is the separated (non-bundled) architecture: the API host references your HttpApi / Application / EF layers plus JWT bearer auth, and it does not include the LeptonX theme or the Account login UI. That already removes the whole Lepton / MVC UI surface, which is the main thing you're after.

Two things worth knowing so there are no surprises:

  • Login is the main reason Razor sticks around. The interactive login screen is a Razor Page from the Account module, and OpenIddict renders it for browser-based sign-in. So if this backend still needs users to log in through a browser, keep that in a dedicated Auth Server project and let the API host stay UI-free. If auth is purely token-based (a separate Auth Server / frontend / external IdP handles login), the API host needs no login UI at all.
  • If your goal is literally zero Razor Pages, note that ABP's MVC maps Razor Pages by default and a couple of framework UI modules still ship one (for example the multi-tenancy tenant-switch UI), so you'd also remove those *.Mvc.UI.* modules, not just the LeptonX theme.

To give you the exact modules and packages to strip out for your project, could you share:

  1. ABP version and template (app / microservice, and MVC / Blazor / Angular).
  2. Is your solution single-project (the host also renders the UI and acts as the auth server), or already split into separate HttpApi.Host / AuthServer projects?
  3. Does this backend still need to serve an interactive browser login, or is auth purely token-based?

With that I'll give you the precise list of module DependsOn entries and PackageReferences to remove.

Thanks

Hi,

This is fixed now - the missing source packages are published, so you can download the LeptonX theme source from ABP Studio/CLI again. Please give it another try.

Thanks

Hi,

This is fixed now - the missing source packages are published, so you can download the LeptonX theme source from ABP Studio/CLI again. Please give it another try.

Thanks

Hi,

This is fixed now - the missing source packages are published, so you can download the LeptonX theme source from ABP Studio/CLI again. Please give it another try.

Thanks

Hi,

This is fixed now - the missing source packages are published, so you can download the LeptonX theme source from ABP Studio/CLI again. Please give it another try.

Thanks

Hi,

This isn't an entitlement issue with your account - the source package for that LeptonX theme version wasn't published on our side, which is why Studio shows that message. We've confirmed it and will fix it soon.

We've refunded your ticket.

Thanks

Showing 281 to 290 of 12912 entries
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 November 02, 2026, 06:56
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.