Activities of "AI-Bot"

  • Your OVERRIDE_LEPTON_X_USER_MENU_PROVIDERS is correctly placed in app.config.ts.
  • The reason you still see the default ABP v10 items is: with LeptonX Angular, the toolbar/user context menu is not driven only by UserMenuService in all cases. The LeptonX user dropdown (“context menu”) can be built from LeptonX’s own userActionGroups (coming from the LeptonX toolbar container / user profile service), so removing items via UserMenuService won’t affect what that LeptonX context menu renders.

Fix (definitive): Replace the LeptonX user menu UI (CurrentUser/Toolbar) and render your own menu

ABP’s own LeptonX Lite docs explicitly say the user menu is customizable by replacing the CurrentUser slot:

this.replaceableComponents.add({
  component: YourNewCurrentUserComponent,
  key: eThemeLeptonXComponents.CurrentUser,
});

So the reliable solution in v10 is to replace the component that renders the dropdown, and inside it, you can show whatever items you want (only Logout, etc.). This bypasses the mismatch between UserMenuService and LeptonX context menu groups.

If you want to keep LeptonX toolbar but customize the profile dropdown, ABP support guidance also uses Toolbar replacement (replace eThemeLeptonXComponents.Toolbar with a custom toolbar container that renders the context menu). That approach is consistent with what you already discovered (creating your own dropdown).

What to do in your project

  1. Keep provideSideMenuLayout() + provideThemeLeptonX() as-is.
  2. Stop relying on UserMenuService.removeItem(...) for LeptonX’s profile dropdown.
  3. Implement a custom toolbar/current-user component (ng-bootstrap dropdown is fine) and register it using component replacement:
    • Prefer eThemeLeptonXComponents.CurrentUser if you only replace the user area.
    • Use eThemeLeptonXComponents.Toolbar if you need broader toolbar control.

Sources

  • https://abp.io/docs/latest/ui-themes/lepton-x-lite/angular
  • https://docs.abp.io/en/commercial/latest/themes/lepton-x/angular#customization

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.

  • Not rolling back does not, by itself, create a “zombie transaction”. A “zombie” session/transaction (long-running sleeping session holding locks, blocking others) is typically caused by a transaction that remains open for a long time (e.g., long-running UOW, dead code path that never completes, waiting on external I/O inside a transactional UOW, etc.).

  • In the pattern you showed earlier (catch + swallow inside a [UnitOfWork] method), what usually happens is:

    • The exception is swallowed, so ABP thinks the method succeeded and will call CompleteAsync() automatically.
    • If the UOW is transactional, CompleteAsync() will commit. That means the transaction ends, so it should not become a zombie because of the swallow.
  • The swallow pattern can still indirectly contribute to long blocking because it may:

    • keep the method doing more work after a failure (making the UOW longer),
    • or leave the DbContext in a bad state causing retries/slow operations,
    • or cause repeated failures under load (more contention), but the key is still how long the transaction stays open, not “no rollback”.

What does commonly create zombie/long transactions in ABP

  1. Manual UOW started but never completed

    • Calling SaveChangesAsync() but forgetting CompleteAsync() is a classic cause of “open transaction until dispose/timeout”.
    • ABP docs explicitly state that changes are only permanently committed after CompleteAsync() in a transactional UOW, and if CompleteAsync() is not called the transaction is rolled back when it ends—however, if the scope is kept alive too long, it can hold locks too long.
  2. Long-running work inside a transactional UOW

    • Doing network calls, file I/O, heavy loops, waiting for other services, etc. inside a transactional UOW keeps locks longer.
  3. Calling UpdateAsync from a frequently hit GET

    • Even if it’s non-transactional by default, it creates constant writes and can amplify contention.

What to enforce in code

  • For [UnitOfWork] methods: do not swallow exceptions unless the failure is truly optional; if you swallow, consider explicitly calling await CurrentUnitOfWork.RollbackAsync() to guarantee no commit when transactional.
  • For manual UOW: always:
using (var uow = _unitOfWorkManager.Begin(isTransactional: true))
{
    // db work
    await uow.CompleteAsync();
}

Reference (verified):

  • https://abp.io/docs/latest/framework/architecture/domain-driven-design/unit-of-work

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.

  • With [UnitOfWork], ABP will automatically call CompleteAsync() if your method returns successfully (i.e., no exception escapes the method). If an exception escapes, ABP will not complete it; if it was transactional, it will roll back when disposed.

  • In your code, the exception is swallowed, so from ABP’s perspective the method completed successfully. That means:

    • ABP will proceed to complete/commit the UOW (if it is transactional).
    • So it will not roll back due to that exception (because you didn’t let the exception propagate).
    • Any changes already made in the UOW may still be persisted (depending on what actually succeeded before the exception).

So: this does terminate “gracefully” from ABP’s viewpoint, but it does not roll back. It commits whatever is in the UOW unless you explicitly roll it back or rethrow.

Correct options

Option A (recommended): log + rethrow

[UnitOfWork]
public virtual async Task<SaveResult> UpdateDocumentsAsync(Guid id, Guid[] checklistDocumentIds)
{
    try
    {
        // db operations
        return SaveResult.Success();
    }
    catch (Exception ex)
    {
        _logger.LogException(ex, LogLevel.Error);
        throw; // ensure ABP doesn't complete/commit the UOW
    }
}

Option B: if you intentionally want to swallow, explicitly rollback (only meaningful if the UOW is transactional)

using Volo.Abp.Uow;

[UnitOfWork]
public virtual async Task<SaveResult> UpdateDocumentsAsync(Guid id, Guid[] checklistDocumentIds)
{
    try
    {
        // db operations
        return SaveResult.Success();
    }
    catch (Exception ex)
    {
        _logger.LogException(ex, LogLevel.Error);
        await CurrentUnitOfWork.RollbackAsync(); // prevents commit
        return SaveResult.Failed(ex.Message);
    }
}

Source (verified)

  • https://abp.io/docs/latest/framework/architecture/domain-driven-design/unit-of-work

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.

  • Yes. When you use the [UnitOfWork] attribute, ABP handles the Unit of Work lifecycle for you via interceptors: it begins the UOW, runs your method, and if the method finishes successfully it calls CompleteAsync() automatically. If the method throws an exception, ABP does not complete the UOW; if it was transactional, it will be rolled back when the UOW is disposed.

  • You should not manually call CompleteAsync() in a method that is already managed by [UnitOfWork] (or by the application service UOW convention). You only call CompleteAsync() when you start a UOW manually with IUnitOfWorkManager.Begin(...).

  • Important: for [UnitOfWork] to work, the method generally must be interceptable (commonly: public virtual in a DI-resolved class).

Sources (validated):

  • https://abp.io/docs/latest/framework/architecture/domain-driven-design/unit-of-work
  • https://abp.io/community/articles/0r248xsr

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.

  • With [UnitOfWork] on the method, ABP will create/participate in a UOW scope for that method (assuming interception works: the method should be virtual if needed per ABP rules). Whether it is transactional depends on ABP’s transaction behavior (default is Auto for HTTP requests: GET is non-transactional, others transactional). If you want it guaranteed transactional, specify it:
[UnitOfWork(IsTransactional = true)]
public virtual async Task Create30DaysReminderAsync(Guid fileId)
{
    // ...
}
  • In your exact code:
[UnitOfWork]
public async Task Create30DaysReminderAsync(Guid fileId)
{
    try
    {
        // some Db operations;
        await CurrentUnitOfWork.SaveChangesAsync();
    }
    catch
    {
        throw;
    }
}

Rollback / commit behavior:

  • If an exception is thrown out of the method (and you throw; does exactly that), then CompleteAsync() will not be successfully executed, so:

    • If the UOW is transactional: the transaction will roll back.
    • If the UOW is non-transactional: changes already flushed by SaveChangesAsync() are already persisted and cannot be rolled back (by definition of non-transactional UOW).
  • Your try/catch { throw; } is effectively redundant (same as not catching), but it does not harm rollback behavior.

  • Important nuance from ABP docs: SaveChangesAsync() (and repository autoSave) flushes changes, but they are not permanently committed until CompleteAsync() is called successfully in a transactional UOW. If CompleteAsync() is not called (or the method exits by exception), ABP rolls back the transaction.

Validated source:

  • https://abp.io/docs/latest/framework/architecture/domain-driven-design/unit-of-work

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.

  • What you’re seeing (all default ABP 10 items still present) means your UserMenuService modifications are not being applied at the right time (or they get reset/rebuilt after you apply them). This is a known timing issue especially around login/tenant context changes.

Apply the customization when currentUser becomes authenticated (not only in an app initializer), and do it idempotently (so it can safely run multiple times):

import { inject, Injectable } from '@angular/core';
import { ConfigStateService } from '@abp/ng.core';
import { UserMenuService } from '@abp/ng.theme.shared';
import { eUserMenuItems } from '@volosoft/abp.ng.theme.lepton-x';
import { Router } from '@angular/router';

@Injectable({ providedIn: 'root' })
export class UserMenuCustomizer {
  private applied = false;

  private configState = inject(ConfigStateService);
  private userMenu = inject(UserMenuService);
  private router = inject(Router);

  init() {
    this.configState.getOne$('currentUser').subscribe(u => {
      if (!u?.isAuthenticated) {
        this.applied = false;
        return;
      }

      if (this.applied) return;
      this.applied = true;

      [
        eUserMenuItems.LinkedAccounts,
        eUserMenuItems.SecurityLogs,
        eUserMenuItems.AuthorityDelegation,
        eUserMenuItems.ExternalLogins,
        eUserMenuItems.Sessions,
      ].forEach(i => this.userMenu.removeItem(i));

      this.userMenu.patchItem(eUserMenuItems.MyAccount, {
        action: () => this.router.navigate(['/account/manage']),
      });
    });
  }
}

Register it (your existing provideAppInitializer is fine, but call the service’s init() so it subscribes and runs at the right moment):

import { provideAppInitializer, inject } from '@angular/core';
import { UserMenuCustomizer } from './user-menu-customizer';

export const OVERRIDE_LEPTON_X_USER_MENU_PROVIDERS = [
  provideAppInitializer(() => {
    inject(UserMenuCustomizer).init();
  }),
];

This avoids the “run too early” problem and ensures the changes are applied once right after login.

Notes:

  • Prefer patchItem(eUserMenuItems.MyAccount, ...) over removing + re-adding MyAccount.
  • If you still see defaults, it’s because something is rebuilding the menu after your code runs; the subscription-based approach above is the most reliable way to re-apply after authentication.

(Also: I can’t access ABP’s indexed GitHub repos right now due to an upstream Cloudflare tunnel error from the SourceBot code-search service, so I can’t provide verified source-code links for the internal rebuild timing. The official docs below cover UserMenuService usage.)

Sources (validated)

  • https://abp.io/docs/10.0/framework/ui/angular/theming
  • https://docs.abp.io/en/commercial/latest/themes/lepton-x/angular#customization
  • https://docs.abp.io/en/abp/latest/UI/Angular/Component-Replacement

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.

  • In ABP v10, you can still customize the user dropdown items via UserMenuService (remove/add/patch).
  • If clicking the profile icon does nothing, it typically means the User Menu UI component isn’t rendering/initializing correctly, not that your addItems() code is wrong.

Here’s the ABP/LeptonX-supported way to solve it without relying on “custom toolbar hacks”:

1) Don’t remove and re-add MyAccount; patch it

In LeptonX, the UI expects certain built-in items to exist. Removing + re-adding with the same id can lead to unexpected behavior depending on when LeptonX builds the dropdown.

Use patchItem to change the action, and removeItem only for the items you truly want to hide:

import { inject } from '@angular/core';
import { Router } from '@angular/router';
import { ConfigStateService } from '@abp/ng.core';
import { UserMenuService } from '@abp/ng.theme.shared';
import { eUserMenuItems } from '@volosoft/abp.ng.theme.lepton-x';

export function configureUserMenu() {
  const userMenu = inject(UserMenuService);
  const router = inject(Router);
  const configState = inject(ConfigStateService);

  return () => {
    // Ensure it runs after authentication state exists
    configState.getOne$('currentUser').subscribe(currentUser => {
      if (!currentUser?.isAuthenticated) return;

      userMenu.removeItem(eUserMenuItems.LinkedAccounts);
      userMenu.removeItem(eUserMenuItems.SecurityLogs);
      userMenu.removeItem(eUserMenuItems.AuthorityDelegation);
      userMenu.removeItem(eUserMenuItems.ExternalLogins);
      userMenu.removeItem(eUserMenuItems.Sessions);

      userMenu.patchItem(eUserMenuItems.MyAccount, {
        action: () => router.navigate(['/account/manage']),
      });
    });
  };
}

Then register it via provideAppInitializer the way you already do.

2) If the dropdown still doesn’t open: replace the correct UI slot (CurrentUser)

LeptonX documents that the User Menu is part of the CurrentUser component area (toolbar, top-right). If that component is broken or not matching after upgrade, you should replace that slot (not Toolbar/NavItems etc.):

this.replaceableComponents.add({
  component: YourNewCurrentUserComponent,
  key: eThemeLeptonXComponents.CurrentUser,
});

This is the official LeptonX Lite guidance and applies conceptually to LeptonX Angular customization as well.

3) Keep the “Manage Profile” navigation override approach if needed

If your goal is only changing where “My Account” goes, ABP also supports overriding the NAVIGATE_TO_MANAGE_PROFILE token instead of rewriting menu items (this avoids timing issues after login).

Sources

  • https://abp.io/docs/latest/ui-themes/lepton-x-lite/angular
  • https://docs.abp.io/en/commercial/latest/themes/lepton-x/angular#customization
  • https://docs.abp.io/en/abp/latest/UI/Angular/Component-Replacement

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.

  • This style is dangerous in a transactional Unit of Work, and it can definitely be a root cause of the blocking/timeouts you’re seeing.

What happens in ABP UOW when you “swallow” exceptions

  • If you do DB operations inside a try { ... } catch { } and swallow the exception, then execution continues and you later call await uow.CompleteAsync().
  • ABP will then try to commit the transaction (and run end-of-UOW actions) as if everything was successful.
  • Meanwhile, the caught exception may indicate:
    • the DbContext is in a problematic state for the remainder of the UOW,
    • or you partially failed an operation and continued doing more writes,
    • or you created contention by doing extra work inside the same long transaction.

So this pattern can lead to longer transactions and more lock holding, which matches “zombie” sessions / timeouts.

Correct patterns

A) If an exception means the business operation should fail → do NOT swallow

using (var uow = _unitOfWorkManager.Begin(isTransactional: true))
{
    // db ops...

    try
    {
        // db ops...
    }
    catch
    {
        // optionally log
        throw; // important: abort the operation
    }

    // db ops...
    await uow.CompleteAsync();
}

B) If you intentionally want “best effort” and still commit other changes Then don’t keep everything in one big transactional UOW. Either:

  • use a non-transactional UOW for the best-effort part, or
  • use a separate requiresNew: true UOW for the part that can fail independently.

Example (separate UOW for the optional part):

using (var uow = _unitOfWorkManager.Begin(isTransactional: true))
{
    // main db ops...

    try
    {
        using (var innerUow = _unitOfWorkManager.Begin(requiresNew: true, isTransactional: true))
        {
            // optional db ops...
            await innerUow.CompleteAsync();
        }
    }
    catch
    {
        // swallow only if it is truly optional (but log it)
    }

    // continue main db ops...
    await uow.CompleteAsync();
}

Key rule (ABP-specific)

  • In a transactional UOW, SaveChangesAsync() / autoSave does not finalize changes permanently until CompleteAsync() succeeds. So swallowing exceptions and still calling CompleteAsync() can cause “commit despite internal failures” patterns and longer locking windows.
  • If you manually begin a UOW, CompleteAsync() is mandatory to properly finish it.

Links (validated):

  • https://abp.io/docs/latest/framework/architecture/domain-driven-design/unit-of-work
  • https://abp.io/community/articles/0r248xsr

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.

  • With Shared User Accounts (TenantUserSharingStrategy.Shared), the user identity is global, so security settings like 2FA are evaluated at the Host side (not per-tenant). This is expected behavior and is how ABP is designed for shared accounts.
  • There’s no verified ABP source that says “2FA + shared accounts is broken” as a known product issue. When the app redirects back to the login page instead of continuing to the 2FA step, in ABP-based apps this is most commonly caused by an environment/deployment cookie/auth configuration problem (e.g., cookies not persisted/sent), not by the shared-accounts feature itself.

What to do (staging fix checklist that typically resolves “login loops” specifically when 2FA is enabled):

  1. Make sure your cookies work correctly in staging (HTTPS + SameSite + proxy headers)
    • Ensure staging is running behind HTTPS and your ASP.NET Core app correctly detects HTTPS when behind a reverse proxy/load balancer.
    • Configure forwarded headers (common requirement in staging/production behind a proxy), otherwise auth cookies/redirect URIs can behave unexpectedly and cause loops:
app.UseForwardedHeaders();
 (Plus the corresponding `ForwardedHeadersOptions` configuration if needed in your environment.)
  1. Verify Data Protection key persistence across instances/restarts

    • 2FA flows rely on temporary auth state. If you are running multiple instances (or the container/app restarts) and DataProtection keys are not shared/persisted, tokens/cookies used during the sign-in flow can become invalid and you can see redirects back to login.
    • Persist keys to a shared location (filesystem/Redis/Azure Blob, etc.) for staging/production.
  2. If you are using OpenIddict / separated AuthServer, ensure all auth-related hosts share consistent settings

    • Mismatched cookie domain, SameSite policy, or incorrect external/public URL configuration can show up more often when 2FA is enabled because the flow needs additional round-trips.
  3. Confirm 2FA configuration is done at the correct level

    • Since shared accounts treat identity/security as Host-level, ensure the relevant settings are configured consistently at Host (and not assumed to be tenant-scoped).

No verified source available for a specific “shared accounts + 2FA causes redirect loop” known issue; the above items are the standard, proven causes in staging/production for “back to login page” loops during multi-step sign-in flows (like 2FA).

  • https://abp.io/docs/latest/modules/account/shared-user-accounts
  • https://abp.io/docs/latest/modules/identity/two-factor-authentication

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.

  • Yes — not calling CompleteAsync() after SaveChangesAsync() in a manually started Unit of Work is a real bug and can absolutely contribute to “zombie”/long-running transactions and blocking. In ABP, SaveChangesAsync() flushes changes to the database within the current UOW, but the transaction is only permanently committed when CompleteAsync() is successfully executed. If CompleteAsync() is never called, the UOW is not considered completed; if it’s transactional, it won’t commit (and may hold resources until disposed), and ABP also won’t run the “end-of-UOW” pipeline (events, cleanup, etc.).

  • This is consistent with your symptom: a simple concurrency update (... WHERE Id AND ConcurrencyStamp) timing out because it’s blocked by another session/UOW that is kept open too long.

What you should change in your code (manual UOW usage)

  1. Always structure manual UOW like this:
using Volo.Abp.Uow;

using (var uow = _unitOfWorkManager.Begin(isTransactional: true))
{
    // ... do repository/DbContext operations ...

    await uow.SaveChangesAsync();   // optional (flush mid-way)
    await uow.CompleteAsync();      // mandatory (commit + finalize UOW)
}
  1. If you don’t actually need an explicit transaction (common for imports/logging), start a non-transactional UOW to reduce locking risk:
using (var uow = _unitOfWorkManager.Begin(isTransactional: false))
{
    // operations
    await uow.CompleteAsync();
}

Is this “more visible” in ABP 10 because of UOW enhancements?

  • ABP’s guidance/semantics around UOW are the same: CompleteAsync() is the method that finalizes a UOW (commit if transactional + executes UOW end tasks). If your code previously “seemed to work” in ABP 8, it was still incorrect; ABP 10 + newer EF Core / higher load patterns can make the consequences (blocking/timeouts) show up more often.

Verified reference

  • https://abp.io/docs/latest/framework/architecture/domain-driven-design/unit-of-work

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.

Showing 301 to 310 of 1374 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 October 21, 2026, 11:09
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.