We're running ABP Framework 10.4 with an Angular 21 frontend. Our angular/ project uses Yarn Berry 4.12.0 (nodeLinker: node-modules in .yarnrc.yml), not Yarn Classic — this is required for our dependency setup (private npm registry scopes for FontAwesome Pro, etc.).
The problem: abp install-libs appears to only support/detect Yarn Classic 1.x, so our local setup script has to explicitly switch Yarn versions back and forth via Corepack around it:
corepack prepare yarn@1.22.22 --activate
abp install-libs
corepack prepare yarn@4.12.0 --activate
yarn install
This is fragile in practice: abp install-libs internally runs an unfrozen yarn install under whichever Yarn version is currently active. When it runs under Yarn 1, it silently rewrites yarn.lock back into the old v1 lockfile format. If the script is ever interrupted, or someone runs abp install-libs in isolation without the follow-up corepack prepare yarn@4.x --activate && yarn install, the repository can end up with a Yarn-1-format lockfile committed on top of a Yarn-Berry project — completely silently, since a plain local yarn install doesn't complain about it. This happened to us once and was hard to trace back.
Questions:
abp install-libs work with an already-active Yarn Berry installation, instead of requiring Yarn 1 specifically?install-libs is meant for MVC/Razor Pages/Blazor Server only — is it actually required at all for an Angular project, or is calling it in our setup script simply unnecessary legacy boilerplate we can drop?Any clarification would help us remove this two-Yarn-version workaround entirely.
Thank you very much for the detailed explanation and code example :-) At the moment, we still need a bit more time to implement this. It isn’t a top priority just yet.
Wouldn’t that be a great feature for an extension to the ABP framework? A generic solution like that would probably be useful to many people and would be a useful addition. Please add it to the wish list :-)
We’d like to implement an undo/redo feature in our application. I’m aware that this can be a complex issue, particularly when dealing with dependent entities. And yet it’s a feature for which most applications require a solution.
Have you written anything on this subject before? (I haven’t been able to find anything, unfortunately)
How would you implement this using the existing ABP functionality?
EntityChanges and EntityPropertyChanges would certainly be one possible approach, but this is probably not quite enough to implement the functionality fully.
Would EF’s change tracking be suitable for storing the complete changes of a transaction, e.g. in a cache? Or does that stop working once you’ve called SaveChanges?
Do you have any ideas on how you would solve this (best practices) and whether there might already be something implemented in ABP that could be used?
Another big question is how to go about this when ‘delete cascading’ is configured… or when the entities are not fully audited entities.
I look forward to your answer.
Best, Adrian
Perfect, thank you :-)
A while back, I posted two feature requests under “ABP Suite feature requests”. Do you know if they were noted and, if so, whether they'll be implemented at some point?
https://abp.io/support/questions/8803/ABP-Suite-feature-request--2025#answer-3a20f067-49ef-b56a-6a66-0d1671aa7bf2
https://abp.io/support/questions/8803/ABP-Suite-feature-request--2025#answer-3a20f070-f3b8-a78e-2794-b2edbaa436b7
Thanks for the feedback.
I've now successfully implemented it using a ...PagedListInput (the existing class) and ...ListInput (the new class with ISortedResultRequest). And I wanted to do it “correctly” from the start using the extended partial class, so I've also adapted that template.
A somewhat clunky but probably functional workaround might be to define a second input class in GetItemsInput.txt. However, I would prefer other solutions.
Server.AppService.GetItemsInput.txt:
using Volo.Abp.Application.Dtos;
using System;
namespace %%solution-namespace%%%%<if:ApplicationContractsNotExists>%%%%.AppServices%%</if:ApplicationContractsNotExists>%%.%%microservice-dtos-namespace-prefix%%%%entity-namespace%%
{
public %%custom-code-abstract-modifier%% class Get%%entity-name-plural%%PagedInput%%custom-code-base%% : PagedAndSortedResultRequestDto
{
public string? FilterText { get; set; }
%%property-definitions%%
public Get%%entity-name-plural%%PagedInput%%custom-code-base%%()
{
}
}
public %%custom-code-abstract-modifier%% class Get%%entity-name-plural%%Input%%custom-code-base%%
{
public string? FilterText { get; set; }
%%property-definitions%%
public Get%%entity-name-plural%%Input%%custom-code-base%%()
{
}
}
}
What we really miss in the entities generated by the ABP Suite is a GetList-Method that returns the data without pagination. By customizing certain templates, we have now added these missing methods ourselves.
Here are some examples of our customizations:
Server.Controller.ItemController.txt:
[HttpGet]
public virtual Task<List<%%entity-name%%Dto>> GetListAsync(Get%%entity-name-plural%%Input input)
{
return _%%entity-name-plural-camelcase%%AppService.GetListAsync(input);
}
[HttpGet]
[Route("paged")]
public virtual Task<PagedResultDto<%%entity-name%%Dto>> GetPagedListAsync(Get%%entity-name-plural%%Input input)
{
return _%%entity-name-plural-camelcase%%AppService.GetPagedListAsync(input);
}
Server.AppService.AppService.txt:
public virtual async Task<List<%%entity-name%%Dto>> GetListAsync(Get%%entity-name-plural%%Input input)
{
var items = await _%%entity-name-camelcase%%Repository.GetListAsync(input.FilterText, %%input-properties%%%%<if:HasFilterableProperties>%%, %%</if:HasFilterableProperties>%%input.Sorting);
return ObjectMapper.Map<List<%%entity-name%%>, List<%%entity-name%%Dto>>(items);
}
public virtual async Task<PagedResultDto<%%entity-name%%Dto>> GetPagedListAsync(Get%%entity-name-plural%%Input input)
{
var totalCount = await _%%entity-name-camelcase%%Repository.GetCountAsync(input.FilterText%%<if:HasFilterableProperties>%%, %%</if:HasFilterableProperties>%%%%input-properties%%);
var items = await _%%entity-name-camelcase%%Repository.GetListAsync(input.FilterText, %%input-properties%%%%<if:HasFilterableProperties>%%, %%</if:HasFilterableProperties>%%input.Sorting, input.MaxResultCount, input.SkipCount);
return new PagedResultDto<%%entity-name%%Dto>
{
TotalCount = totalCount,
Items = ObjectMapper.Map<List<%%entity-name%%>, List<%%entity-name%%Dto>>(items)
};
}
Here's my problem / my question:
For my GetListAsync method, I used the same input class that’s also used for the paged list. The problem, however, is that Angular requires a value to be set for MaxResultCount, even if that value isn’t used. This is poor design and confuses developers.
How could I use the ABP Suite to generate a custom input class for this? Ideally, of course, I’d be able to create a Server.AppService.GetItemsPagedInput.txt file in addition to the Server.AppService.GetItemsInput.txt template.
Is it possible to create your own templates? If so, where would they be saved?
Thanks, Adrian
Environment:
Great, it works :-D
Thank you so much for the quick help! I wish I'd asked much sooner ;-)
Hi, thanks for this detailed answer. This is exactly the kind of solution I was looking for. I’ll be happy to test it out, and I think this is what I need.
_installer.InstallAsync() is relatively complex (recursive traversal and inserts across multiple repositories), so I can’t share it here. But if your approach doesn’t work, I’ll send you the code via email.
By the way: after 1000 inserts, I call await _unitOfWorkManager.Current!.SaveChangesAsync(). I also clear the DbContext’s ChangeTracker. The data is also fully present in the database. That’s why I suspect the problem might be in postprocessing (event pipeline).