Activities of "maliming"

Hi,

Thanks for the suggestion. You're right, [DynamicPropertyUnique] is specific to ABP Low-Code's dynamic entities; ABP Suite's entity designer doesn't have a "unique" option today. We'll pass this on to the Suite team as a feature request.

For now, uniqueness needs to be configured manually: add a unique index at the database level (for EF Core, b.HasIndex(x => x.ProductCode).IsUnique() plus a new migration), and check for duplicates in your app service or manager to show a friendly validation message. Put this mapping in your own model-configuration extension method (in a separate file) and call it from OnModelCreating, instead of editing the Suite-generated builder.Entity<...>(...) block — Suite removes and re-creates those blocks when you regenerate the entity.

Thanks

Hi,

Yes, this is the right place for these reports — thanks for the detailed analysis.

You're right, this is a gap in the theme CSS: there is no rule integrating .custom-select-wrapper into .input-group, unlike the existing DataTables / validation / .form-floating special cases. We'll fix the custom select's .input-group integration in the theme (both the Blazor and MVC packages) in the next patch release, and we'll evaluate the opt-out hook for FormSelect.init together with it, since component-library scenarios like Blazorise need the native element.

Until the patch is out, this app-side CSS keeps the custom select and restores the group layout:

.input-group > .custom-select-wrapper {
  flex: 1 1 auto;
  width: 1%;
  min-width: 0;
  margin-bottom: 0;
}

/* match Bootstrap's corner flattening for grouped controls */
.input-group > .custom-select-wrapper:not(:first-child) > .custom-select-display {
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}

.input-group > .custom-select-wrapper:not(:last-child) > .custom-select-display {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}

Your native-select restore is still the right call where inline widths on the <select> matter, so feel free to keep that instead.

One correction on the sizing point: the .input-group issue is still present in 5.6.0, but the separate form-select-sm / form-select-lg issue was fixed there — the generated display is now styled based on those classes. Inline styles on the native select are still not transferred; an opt-out hook would let those selects stay native and keep their own styles.

Your ticket has been refunded.

Thanks

Hi,

You're right, this is a bug. The Admin Console shell is pre-built with root-absolute /admin-console/... URLs and the middleware serves it as-is, so it can't work under a non-root PathBase, and there is no configuration in 10.5.0 that can change it.

Your response-rewriting middleware is a reasonable interim solution, but it covers the shell only. The SPA bundle also resolves the default API base to the root-absolute /api, and its router basename is fixed at build time. With app.UsePathBase("/sub") locally this doesn't show up, because requests without the /sub prefix are still answered by the same app — but in a real IIS virtual application, the module API calls (Identity, Settings, etc.) go to the site root. Worth double-checking those pages in your Staging/Demo deployments.

We're preparing a fix along the lines you suggested: the middleware will prefix the <base href> and asset URLs with Request.PathBase when serving the shell, the config endpoint will return a PathBase-aware authority and application root, and the SPA will derive its router basename and API base at runtime instead of baking them in at build time. Once it's released you can delete your middleware; we'll post the version here when it's confirmed.

Your ticket has been refunded. And thanks for the note about the missing React option in the question form — we'll forward it to the team.

Thanks

: )

Hi,

The error popup comes from the dotnet ef migrations add step. It compiles the solution first, but the actual compiler errors are not shown in that popup, so we need the real build output to find the root cause.

Could you share the following:

  1. Run dotnet build in the solution root folder and share the error output.
  2. Your UI framework and database provider (MVC / Angular / Blazor, EF Core / MongoDB).
  3. The first screenshot shows 3 navigation properties on this entity. Do all of them target IdentityUser.cs, or do some of them target your own entities?

You can also send the full build log to liming.ma@volosoft.com if it's too large for this thread.

Thanks

Hi,

The exit reason is written to the ABP Studio log file. Could you check it and share the content:

  • Open Finder → Go → Go to Folder… → enter ~/.abp/studio/ui/logs
  • Share the last lines of abp-studio-ui-logs.txt (the entries around the time you started the app)

If that file doesn't exist or has no new entries, the app is crashing before logging starts. In that case, run it from the terminal and share the output here:

"/Applications/ABP Studio.app/Contents/MacOS/Volo.Abp.Studio.UI.Host"

Also, could you tell us which ABP Studio version you downloaded and your macOS version?

You can send the logs to liming.ma@volosoft.com if they're too large for this thread.

Thanks

Hi,

Quick note first: option 2 doesn't get Department into Suite's cross-module picker — that picker only lists aggregate roots. So option 2 is about how you wire it up, and the right wiring depends mostly on one thing: does a transaction need to save the selected department, or only show/pick departments?

  • If a transaction stores the chosen department (a DepartmentId on the transaction record), then Department is being referenced as an entity in its own right. In ABP that's the sign it should be modeled as an aggregate root rather than a child — decide it from the domain (does it have its own lifecycle and consistency boundary?), not just to get it into the picker. Once it's an aggregate root, Suite generates the reference the same way it did for Company, and it can still keep its owner id (e.g. CompanyId) to record which master it belongs to.
  • If departments are only shown/selected under a chosen master and never stored on the transaction, keep Department as a child. Load the list through the Master Data module — Suite already generates a service that returns a child's records by its master's id — and don't create a direct navigation to the child from the Transaction side.

One thing that affects the answer either way: cross-module references stay clean when the Master Data module exposes what others need through its own service/contract, rather than the Transaction module reaching directly into Department's tables — this matters especially if the two modules might run as separate apps or databases.

So I can give you concrete steps, could you tell me:

  1. Which entity owns Department (which master is it a child of)?
  2. Does a transaction need to save the selected department, or only display/pick it?
  3. Are both modules in one app sharing the same database, or separate services/databases — and is the UI MVC, Angular, or Blazor?

Thanks

Answer

Hi,

Okta is a standard OpenID Connect provider, so you can add it as an external login provider. Add the Microsoft.AspNetCore.Authentication.OpenIdConnect NuGet package (pick the version matching your .NET version) to your Blazor Server project and configure it in the ConfigureServices method of your web module:

using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;

context.Services.AddAuthentication()
    .AddOpenIdConnect("Okta", "Okta", options =>
    {
        options.Authority = "<your-okta-issuer>"; // e.g. https://<your-domain>.okta.com/oauth2/default
        options.ClientId = "<okta-client-id>";
        options.ClientSecret = "<okta-client-secret>";
        options.ResponseType = OpenIdConnectResponseType.Code;
        options.GetClaimsFromUserInfoEndpoint = true;
        options.Scope.Add("email");
        options.CallbackPath = "/signin-okta";
        options.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "sub");
    });

On the Okta side, create a Web Application integration, set its sign-in redirect URI to https://<your-app-host>/signin-okta (it must match the CallbackPath above), and assign the users/groups that will use it. For the Authority, use the issuer of the Okta authorization server you use (you can see it in the Okta admin panel, e.g. https://<your-domain>.okta.com/oauth2/default for the default authorization server, or https://<your-domain>.okta.com for the org authorization server). Keep the client secret in appsettings.json/user-secrets instead of the source code. After this, an Okta button will show up on the login page automatically.

If you also want to manage the client id/secret on the Settings page of the UI (per tenant), see the dynamic external provider setup: https://abp.io/docs/latest/modules/account-pro#install-a-new-external-login

For the Enable two factor authentication option on the Users page: you can leave it as is. Users signing in via Okta won't get ABP's local two-factor prompt — MFA for them should be enforced by your Okta sign-on policies (adding Okta as a login provider doesn't enable MFA by itself). The local two-factor option still applies to users who sign in with a local username/password, so if you keep local login enabled, configure it as you do today for those users.

For the Applications page: no changes are needed there for Okta. Those records are the clients of your own OpenIddict server (your Blazor Server app with its signin-oidc redirect uri), while Okta is an external provider in front of the login page — the two are independent. The checked flows in your screenshot (authorization code + implicit + hybrid) are the normal setup for the Blazor Server client, since it uses the hybrid flow — keep them as they are so they continue to match your client configuration.

If you run into any errors during the setup, just share the details here and we'll help.

Thanks

Hi,

This is by design. When "Include entities from ABP modules" is enabled, Suite's module-entity picker only offers aggregate roots as navigation targets — child entities aren't listed by that picker, which is why Department doesn't show up while it's modeled as a child.

It comes down to how Department is modeled:

  • If it's genuinely an independent concept in your domain (its own lifecycle and consistency boundary), make it an aggregate root. If it already uses a Guid key, use AggregateRoot<Guid>, AuditedAggregateRoot<Guid>, or FullAuditedAggregateRoot<Guid>, and give it a public string property like Name for the display field — then rebuild the module and restart Suite before selecting it again. (The picker reads module entity keys as Guid, so don't switch a non-Guid domain key just to make it appear.)
  • If it's really a child of another aggregate, keep it that way and work with it through its owning aggregate root (or that module's application services), instead of selecting it as a direct navigation target.

Thanks

Hi,

Yes, this is achievable, and your existing UserCompany / UserOffice / UserStore tables fit this well. ABP permissions control "can the user call this action", they don't filter rows. For "which rows can the user see", use ABP's data filtering system: you add a custom EF Core global query filter to your DbContext, and regular EF Core queries on those entities (app services, APIs, dropdown lookup endpoints) apply the filter by default. It's a standard customization, not a built-in switch.

Define an interface per dimension and implement it on the entities that must be restricted (don't implement it on the UserCompany mapping entity itself, that would make the filter reference itself):

public interface IHasCompany
{
    int CompanyId { get; }
}

public class CustomerOrder : FullAuditedAggregateRoot<Guid>, IHasCompany
{
    public int CompanyId { get; set; }
    // ...
}

Then add the following members to your existing DbContext class (replace int with your actual key type):

using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Users;

public DbSet<Company> Companies { get; set; }
public DbSet<UserCompany> UserCompanies { get; set; }

protected bool IsCompanyFilterEnabled => DataFilter?.IsEnabled<IHasCompany>() ?? false;

protected Guid? CurrentUserId => LazyServiceProvider?.LazyGetRequiredService<ICurrentUser>().Id;

protected override bool ShouldFilterEntity<TEntity>(IMutableEntityType entityType)
{
    if (typeof(IHasCompany).IsAssignableFrom(typeof(TEntity)) ||
        typeof(TEntity) == typeof(Company))
    {
        return true;
    }

    return base.ShouldFilterEntity<TEntity>(entityType);
}

protected override Expression<Func<TEntity, bool>>? CreateFilterExpression<TEntity>(
    ModelBuilder modelBuilder,
    EntityTypeBuilder<TEntity> entityTypeBuilder)
    where TEntity : class
{
    var expression = base.CreateFilterExpression<TEntity>(modelBuilder, entityTypeBuilder);

    // Business entities (CustomerOrder etc.): only rows of the user's companies
    if (typeof(IHasCompany).IsAssignableFrom(typeof(TEntity)))
    {
        Expression<Func<TEntity, bool>> companyFilter = e =>
            !IsCompanyFilterEnabled ||
            (CurrentUserId != null &&
             UserCompanies.Any(uc => uc.UserId == CurrentUserId &&
                                     uc.CompanyId == EF.Property<int>(e, nameof(IHasCompany.CompanyId))));

        expression = expression == null
            ? companyFilter
            : QueryFilterExpressionHelper.CombineExpressions(expression, companyFilter);
    }

    // The Company master table itself: this makes dropdowns return only allowed companies
    if (typeof(TEntity) == typeof(Company))
    {
        Expression<Func<TEntity, bool>> companyLookupFilter = e =>
            !IsCompanyFilterEnabled ||
            (CurrentUserId != null &&
             UserCompanies.Any(uc => uc.UserId == CurrentUserId &&
                                     uc.CompanyId == EF.Property<int>(e, nameof(Company.Id))));

        expression = expression == null
            ? companyLookupFilter
            : QueryFilterExpressionHelper.CombineExpressions(expression, companyLookupFilter);
    }

    return expression;
}

Repeat the same pattern for IHasOffice / IHasStore with your UserOffice / UserStore tables.

A few notes:

  • The filter is fail-closed: when there is no current user (background jobs, machine-to-machine clients), queries return nothing. For admin screens that need to see everything, check the admin permission first and then disable the filters explicitly with the IDataFilter service: using (_dataFilter.Disable<IHasCompany>()) { ... } (disable all three filters if the query involves all dimensions). Background workers that need all data disable the filters the same way.
  • Global query filters protect reads only. Your create/update methods must also validate the submitted CompanyId / OfficeId / StoreId against the current user's memberships, otherwise a client can bypass the dropdown and post an id directly.
  • The generated SQL is a parameterized EXISTS subquery against UserCompanies, so add a unique index on UserCompany (UserId, CompanyId) (same for the office/store tables).
  • The membership tables must be mapped in the same DbContext as the filtered entities. In a modular solution, apply the same overrides in each module DbContext that contains restricted entities.
  • This community article is a complete end-to-end sample of the same technique: https://abp.io/community/articles/switching-between-organization-units-i5tokpzt It uses a "current organization" variant where the user switches the active organization from a toolbar dropdown; you can borrow that idea if you ever want an explicit company/office switcher instead of showing all authorized data at once.
  • The docs page below and the article show an older CreateFilterExpression(ModelBuilder) signature; on 10.x the method has the extra EntityTypeBuilder<TEntity> parameter as above.

Docs: https://abp.io/docs/latest/framework/infrastructure/data-filtering

Thanks

Showing 211 to 220 of 12921 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.