Activities of "maliming"

Hi,

This isn't a NuGet source / feed / cache issue on your side. When you run abp update without a version, the CLI decides the "latest stable" from a version list we publish, not from NuGet directly. That list hadn't been bumped to 10.5 yet, so the CLI still saw 10.4.1 as the newest and reported everything as up to date. The 10.5 packages themselves are already on NuGet.

We're updating that list now. Give it a little while, then run abp update again and it'll pick up 10.5.

If you'd rather update right away without waiting, pin the version explicitly:

abp update -v 10.5.0 -lv 5.5.0

The -lv 5.5.0 part is needed so the LeptonX packages get updated too (10.5 ships with LeptonX 5.5.0).

Thanks

Hi,

Suite writes the generated keys into a single base localization file — that part is intentional, it doesn't fan them out to every culture file. The values it generates are English defaults ("HybridRegions": "Hybrid Regions" etc.), so it only puts them in the base file and leaves the other cultures for your real translations, instead of dropping English text into it.json / de-DE.json.

The rough edge is the file it chose. Suite looks for en.json first and writes there. Your Localization/Project folder has no en.json, so it falls back to the first *.json it finds in the folder — that's why the keys ended up in de-DE.json. It's not a deliberate pick of German, and it's not even stable across machines, since the file enumeration order depends on the OS/filesystem.

To make it predictable, add an en.json to Localization/Project:

{
  "culture": "en",
  "texts": {}
}

From then on every generation writes the new keys into en.json, and you translate them into it.json, en-GB.json and de-DE.json from there.

To set expectations: there's no option today to write into all culture files at once, or to choose the target file — the en.json file only controls which file gets the keys, the translations themselves are still manual. We'll also review this fallback so it doesn't depend on the arbitrary file order.

Thanks

The symptom

You're signed in with your abp.io account and your organization has a commercial license, but the tools still behave as if you have no license:

  • ABP Studio falls back to the Community edition and shows an "upgrade your license" message on almost every action.
  • ABP CLI fails when creating a pro template or downloading source, with a message like "Could not find your organization! Make sure the owner of your organization set you as a developer...".
  • ABP Suite may behave as unlicensed or fail to load its licensed features.

This is not a bug when the signed-in account isn't assigned as a Developer. All three tools read the license from the account you signed in with, and this is what you get when that account is not a Developer in your organization. (The same "no license" state can also mean an expired license, but if your organization's license is active, the developer-seat check is the reason here.)

Owner vs Developer

An abp.io organization has two separate roles, and they are not the same thing:

  • Owner: manages the organization, buys licenses and allocates developer seats. An Owner cannot write code in ABP projects, cannot download the pro modules / sample projects, and cannot use the pro features of ABP Studio, CLI or Suite.
  • Developer: can write code, download the source code and samples, use the pro tooling, and ask questions on the support website.

So having a commercial license on the organization, or being only an Owner, is not enough. Studio, CLI and Suite all check the same thing: whether your signed-in account holds a Developer seat. If it doesn't, they treat you as unlicensed, they just surface it differently: Studio drops to the Community edition and shows the upgrade dialog, the CLI fails a pro template / download with the "set you as a developer" message, and Suite doesn't load its licensed features. A single person can be both an Owner and a Developer at the same time, most people who buy the license for themselves need to add themselves under both.

How to fix it

  1. Open https://abp.io/my-organizations and check whether the account you use in the tools is listed under Developers (not only under Owners).
  2. If it isn't, an organization owner adds it to the Developers list. You can be an Owner and a Developer at the same time.
  3. Sign out and sign in again in the tool you're using (in ABP Studio, or run abp logout then abp login for the CLI / Suite). This step matters: the developer membership is cached, so just restarting won't pick it up, you have to re-login.

After that the tools see the commercial license and the message goes away.

How to tell which account the tool is using

ABP Studio's upgrade dialog shows the username, email and organization it resolved, and the CLI prints the account it's logged in as. If that email isn't the one holding the developer seat, or the organization is empty, you're signed in with the wrong account, sign out and sign in with the correct one.

Hi,

There's no supported way today to rebuild the .suite/entities metadata from your existing C# classes. But you can still use your hand-written entities in Suite relationships — the trick is that Suite's relationship generators expect a DTO (and, for master-detail, metadata) for the entity on the other end, and the default guesses a name that doesn't exist for a hand-written entity.

For the common case — a Suite entity that references an existing one (e.g. City -> Region) — use a navigation property, not master-detail, and point it at your DTO:

  1. Add the navigation property on the new entity and pick Region as the target entity.
  2. Open the Advanced tab of the navigation dialog. Under Dependent DTO, set Dto name / Dto namespace to a real DTO for Region (browse the .cs file, or type the name/namespace). If you don't already expose one, add a small RegionDto with the properties you want to show.
  3. Generate.

That's it — Region stays exactly as you wrote it (the class and the DTO are untouched), the new entity gets the foreign key and lookup, and everything compiles. The default Dto name is <Entity>Dto, so if that type doesn't exist you get the RegionDto could not be found compile error; setting it to your actual DTO is what fixes it. The generic IRepository<Region> and the entity-to-DTO mapping are resolved automatically, so as long as the DTO's properties line up with the entity you don't need to wire anything else.

Master-detail (child) relationships are different: they read the master from .suite/entities/<Master>.json and, when you generate the child, Suite regenerates the master's own code. So that path expects the master to be a Suite-managed entity — pointing it at a hand-written master will overwrite that entity's code. If you specifically need master-detail on an existing entity, plan to let Suite own it (you can use Load Entity From Database to pull the table, then move any custom logic into the *.Extended partial classes before generating, since the first generation rewrites the Suite-managed files).

On your suggestions: the UI could warn (or filter out) entities that aren't managed by Suite instead of letting you set up a relationship that then fails at generation — that's a fair point. And there's no import/rebuild of .suite metadata from existing code today either. Both make sense for long-running projects and I'll pass them to the team.

Thanks

Hi,

This message comes from the account you sign in to ABP Studio with, not from the organization license itself. Studio needs that account to be a Developer in your organization. Just having a Business license, or being only an Owner, isn't enough. Owners can manage the organization but can't download/write ABP projects, so when Studio doesn't find a developer seat for the signed-in account it falls back to the Community edition and shows the upgrade message.

Could you open https://abp.io/my-organizations and check:

  • Is the account you use in ABP Studio listed under Developers (not only under Owners)?
  • Which email is shown on that upgrade dialog in Studio? That's the account Studio is actually using.

If the account isn't under Developers, an organization owner can add it there (you can be both an Owner and a Developer at the same time). After that, sign out and sign in again in ABP Studio, a plain restart won't refresh the license, you have to re-login.

Let us know what you see and we'll take it from there.

Thanks

Hi,

Yes, that works — marking a property [Required] makes the proxy generate it without the ?.

It helps to know how it behaves on the server before applying it broadly:

  • On value types (int, bool, DateTime, Guid) [Required] is effectively a no-op. A non-nullable value type is never "missing" as far as validation is concerned, so it never triggers a required error — the only visible effect is the non-optional proxy.
  • On reference types it depends on nullable reference types. If your project has NRT enabled (the default in current ABP templates), a non-nullable string is already treated as required by ASP.NET Core model validation, even without the attribute — so adding [Required] there mostly just lines the proxy up with what the server already does. It's the nullable ones (string?) where [Required] adds a genuinely new server-side rule.

The reference-type behavior is documented here: https://learn.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-10.0#non-nullable-reference-types-and-required-attribute

So in your case (Angular-only, typed proxies, values always set) marking the genuinely-required properties is safe. I'd just avoid blanket-adding it to filter/search or partial-update DTOs where some fields are meant to stay optional. There's no global switch, so it stays per property.

Thanks

Hi,

Starting with v10, the Angular proxy generator treats two things separately for each property:

  • whether it's optional (?) — decided only by whether the property is required in the API contract, i.e. it has a [Required] attribute or the C# required modifier
  • whether it's nullable (| null) — decided by whether the C# type itself is nullable

So a plain int/bool without [Required] now comes out as quantity?: number, simply because nothing in the contract marks it as required. It's no longer tied to it being a non-nullable value type, and the same ? rule now applies uniformly to int, bool, string, enum, Guid, etc. Note this is just a TypeScript optional (?), not number | null — you only get | null when the C# type itself is nullable.

This came from this issue: https://github.com/abpframework/abp/issues/24306 (changed in https://github.com/abpframework/abp/pull/24335). The old logic was inconsistent — value types, reference types, strings and enums each followed a different rule for the ?, so the same "not required" property could be optional or not depending only on its type. The new behavior keeps isRequired and isNullable as two independent flags in the API definition, the same way OpenAPI separates required from the type/nullability of a property.

If a property is always present in your responses and you want it back as quantity: number, mark it as required:

public class QuantityByDateDto
{
    [Required]
    public int Quantity { get; set; }

    [Required]
    public DateTime Date { get; set; }
}

[Required] is the cleanest way to do this. The C# required modifier produces the same result, but since it's a language-level constraint it can force you to update existing object initializers / mapping code, so for plain DTOs [Required] is usually less intrusive.

A couple of things worth keeping in mind: there's no global switch to make every non-nullable value type required — it's per property. And this only changes the generated TypeScript contract; the backend still serializes the same JSON as before, so it's not that the server suddenly omits the field. Marking the always-present fields as required also makes your API contract more accurate for any client, not just the TypeScript side.

Thanks

: )

Hi,

Glad it's working now, and nice that you also wired it through the extended partial class, that's the cleaner way to keep it regeneration-safe.

About the two requests from the Suite feature-requests thread: both are noted on our side and we do plan to implement them.

  • Not forcing a string display property on a navigation property: we'll allow picking a non-string property and leave that choice to the developer.
  • Choosing the output folder for a generated entity: we'll add support for this as well.

I can't tie either to a specific release yet, but they're planned and not forgotten.

Thanks

Hi,

That redesign is the durable fix. If you need something for 9.3.5 right now without upgrading, you can replace the inbox processor so a failing handler no longer blocks the events behind it. It skips the failing event for the current run, keeps processing the rest, and retries the failed one on the next cycle (by which point the event it depends on may already be processed):

using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DistributedLocking;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Threading;
using Volo.Abp.Timing;
using Volo.Abp.Uow;

[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IInboxProcessor))]
public class SkipFailingInboxProcessor : InboxProcessor
{
    public SkipFailingInboxProcessor(
        IServiceProvider serviceProvider,
        AbpAsyncTimer timer,
        IDistributedEventBus distributedEventBus,
        IAbpDistributedLock distributedLock,
        IUnitOfWorkManager unitOfWorkManager,
        IClock clock,
        IOptions<AbpEventBusBoxesOptions> eventBusBoxesOptions)
        : base(serviceProvider, timer, distributedEventBus, distributedLock,
               unitOfWorkManager, clock, eventBusBoxesOptions)
    {
    }

    protected override async Task RunAsync()
    {
        if (StoppingToken.IsCancellationRequested)
        {
            return;
        }

        await using (var handle = await DistributedLock.TryAcquireAsync(DistributedLockName, cancellationToken: StoppingToken))
        {
            if (handle == null)
            {
                Logger.LogDebug("Could not obtain the distributed lock: " + DistributedLockName);
                try
                {
                    await Task.Delay(EventBusBoxesOptions.DistributedLockWaitDuration, StoppingToken);
                }
                catch (TaskCanceledException) { }
                return;
            }

            await DeleteOldEventsAsync();

            // Events that failed in this run. Skipping them keeps one bad event
            // from blocking the rest, and prevents a tight loop on the while below.
            var failedEventIds = new HashSet<Guid>();

            while (true)
            {
                var pendingEvents = (await GetWaitingEventsAsync())
                    .Where(x => !failedEventIds.Contains(x.Id))
                    .ToList();

                if (pendingEvents.Count <= 0)
                {
                    break;
                }

                foreach (var waitingEvent in pendingEvents)
                {
                    try
                    {
                        using (var uow = UnitOfWorkManager.Begin(isTransactional: true, requiresNew: true))
                        {
                            await DistributedEventBus
                                .AsSupportsEventBoxes()
                                .ProcessFromInboxAsync(waitingEvent, InboxConfig);

                            await Inbox.MarkAsProcessedAsync(waitingEvent.Id);

                            await uow.CompleteAsync(StoppingToken);
                        }
                    }
                    catch (OperationCanceledException) when (StoppingToken.IsCancellationRequested)
                    {
                        throw; // shutting down: don't treat cancellation as an event failure
                    }
                    catch (Exception ex)
                    {
                        failedEventIds.Add(waitingEvent.Id);
                        Logger.LogWarning(ex, $"Inbox event {waitingEvent.Id:N} failed; skipping it for this run, will retry next cycle.");
                    }
                }
            }
        }
    }
}

A few things to keep in mind, this is a stopgap, not a backport of the 10.0 policy:

  • It skips failed events within the current processor run; it has no backoff and no max-retry/discard. A failing event keeps retrying every cycle (with a warning log each time) until it succeeds, it just no longer blocks the others. The bounded retry-with-backoff and discard is what InboxProcessorFailurePolicy.RetryLater adds in 10.0.
  • The processor reads a page of waiting events (InboxWaitingEventMaxCount, default 1000) ordered by CreationTime. As long as the failing events are a small part of that page, the rest keep flowing. If failing events ever pile up to the full page size, the later events would be blocked again, so you can raise InboxWaitingEventMaxCount if needed.

The durable fix is still the order-insensitive design, this just keeps the inbox moving in the meantime.

Thanks

Showing 311 to 320 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.