When using ABP Studio to regenerate an entity in a project that was migrated from AutoMapper to Mapperly (following https://abp.io/docs/10.0/release-info/migration-guides/AutoMapper-To-Mapperly), the code generator appends duplicate mapper classes to the bottom of ApplicationMappers.cs instead of detecting and updating the existing Mapperly mapper definitions.
This causes the Mapperly source generator to crash, which cascades into dozens of compilation errors.
Product) with all options enabled — verify the project compilesABP Studio appends new mapper classes at the bottom of [ProjectName]ApplicationMappers.cs, even though Mapperly mappers for the same entity already exist earlier in the file. For example, the file ends up with both:
// Original (already existed, ~line 50)
[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)]
public partial class ProductToProductDtoMapper : MapperBase<Product, ProductDto>
{
public override partial ProductDto Map(Product source);
public override partial void Map(Product source, ProductDto destination);
}
// Duplicate appended by ABP Studio (~line 200)
[Mapper]
public partial class ProductToProductDtoMappers : MapperBase<Product, ProductDto>
{
public override partial ProductDto Map(Product source);
public override partial void Map(Product source, ProductDto destination);
}
Note: some duplicates use a slightly different class name (trailing "s"), while others use the exact same class name, producing different error types.
This causes the Mapperly source generator to fail entirely with:
CS8785: Generator 'MapperGenerator' failed to generate source. Exception was of type 'InvalidOperationException' with message 'Sequence contains more than one element'
Because the source generator crashes, none of the ~100 mapper classes in the file receive their generated implementations, producing widespread CS8795 errors ("Partial method must have an implementation part because it has accessibility modifiers").
When unchecking features like "Create user interface" and regenerating, ABP Studio correctly removes those methods from the base interface (e.g., IProductsAppService.cs). However, it does not update or warn about .Extended files that override or call the removed methods, causing additional CS0115 and CS1061 errors.
.Extended files or warn the developer about broken referencesI believe ABP Studio's code generator may be looking for AutoMapper-style CreateMap<>() calls in a Profile class to determine whether mappers exist. In a migrated project, those calls have been replaced by Mapperly MapperBase<> classes, which the generator doesn't recognize — so it treats the entity as having no mappers and generates new ones.
Projects created fresh with ABP 10.0+ (where Mapperly is the default) likely don't have this issue because the generator was designed to work with the mapper format it created.
Manually revert ApplicationMappers.cs from source control to remove the appended duplicates, then manually fix any .Extended files referencing removed methods.
Template: app ABP Studio Version: 2.1.8 (fresh solution) UI Framework: blazor-server Theme: leptonx Database Provider: ef **Database Management System: **sqlserver **Multi-Tenancy: **Yes
ABP Suite reports: "error occurred on DB migration step"
After generation, AbpSolution1MenuContributor.cs contains invalid C# syntax:
context.Menu.AddItem(new ApplicationMenuItem(AbpSolution1Menus.Books, l["Menu:Books"], url: "/books", icon: "fa fa-file-alt", requiredPermissionName: AbpSolution1Permissions.Books.Default)); icon: "fa fa-file-alt" , return Task.CompletedTask; }
ABP Suite appears to have partially removed the Authors menu entry but left orphaned code fragments, corrupting the file.
When removing a navigation property and regenerating:
ABP Suite corrupts the MenuContributor file when regenerating an entity after removing a navigation property, leaving invalid syntax that prevents compilation.
When I use WithDynamicOptions, I have issues that I don't have when using appsettings. I'll start with just ClientId. If I pull from appsettings, it works but if I remark out that line and use the Dynamic Option stored in the gui, I get the error "SecurityTokenInvalidAudienceException: IDX10208: Unable to validate audience. validationParameters.ValidAudience is null or whitespace and validationParameters.ValidAudiences is null." My end goal is to only set ClientId, ClientSecret, and TenantId in the dynamic External Provider properties. Then with TenantId, I'll build the Authority but could use help on how to do that.
.AddOpenIdConnect("EntraId", "Microsoft Entra Id", options =>
{
options.Authority = configuration["Authentication:EntraId:Instance"] + configuration["Authentication:EntraId:TenantId"] + "/v2.0/";
//options.ClientId = configuration["Authentication:EntraId:ClientId"];
options.ClientSecret = configuration["Authentication:EntraId:ClientSecret"];
options.CallbackPath = configuration["Authentication:EntraId:CallbackPath"];
options.ResponseType = OpenIdConnectResponseType.Code;
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("email");
options.ReturnUrlParameter = "returnUrl";
})
.WithDynamicOptions<OpenIdConnectOptions, OpenIdConnectHandler>("EntraId", options =>
{
options.WithProperty(o => o.ClientId);
options.Properties.Add(new ExternalProviderDefinitionProperty
{
PropertyName = "TenantId",
IsSecret = false
});
})
I'm using the code below to set my Entra ID values. I'm using AbpExternalProviderOptions instead of WithDynamicOptions so I can set the Tenant ID in the web gui and then build the Authority URL. The values get set in the database but how do we retrieve the "EntraId" ExternalProviderDefinition so we can access those property values when setting the OpenIdConnection options when using ABP Pro?
` Configure(options => { options.Definitions.Add(new ExternalProviderDefinition { Name = "EntraId", Properties = new List { new ExternalProviderDefinitionProperty { PropertyName = "ClientId", IsSecret = false }, new ExternalProviderDefinitionProperty { PropertyName = "ClientSecret", IsSecret = true }, new ExternalProviderDefinitionProperty { PropertyName = "CallbackPath", IsSecret = false }, new ExternalProviderDefinitionProperty { PropertyName = "DisplayName", IsSecret = false }, new ExternalProviderDefinitionProperty { PropertyName = "Enabled", IsSecret = false },new ExternalProviderDefinitionProperty { PropertyName = "TenantId", IsSecret = false } } });
});`
I figured out how to add it from extended file. I missed that I needed a new class that inherits from my entity's base class. What really confused me was in the documentation it provides this article. But this article was the key article as it provided the key info about creating a new class. Based on that articles breadcrumb, I'm not sure it's linked up correctly. I found it by searching and it really helped.
For anyone else, I'm not sure we can disable toolbar buttons. But once you override and wait for SetToolbarItemsAsync, you can then clear and add back the buttons you need.
protected override async ValueTask SetToolbarItemsAsync()
{
Toolbar.Contributors.Clear();
Toolbar.AddButton("Import users from excel", () =>
{
//TODO: Write your custom code
return Task.CompletedTask;
}, "file-import", Blazorise.Color.Secondary);
}
<br>
Thanks for the suggestions. Is there a way to add my toolbar buttons in the extended.razor.cs so it doesn't get overwritten if we use ABP Suite to make changes later?
On question #2, I'm not trying to change the order but the visibility. Is there a way to hide or disable the default Create button?
Thanks,
John
What's the best way to set the value of a navigational property so the create and edit always have that value. Here's an example. I have a Dealership entity and a Leads entity. Leads has a required navigational property of DealershipId. Here's what I have working.
On the Dealerships razor page, I added and EntityAction.
<EntityAction TItem="DealershipDto" Visible="@CanEditDealership" Clicked="async () => await NavigateToLeads(context)" Text="@L["Leads"]"></EntityAction>
In Dealerships.Extended.razor.cs I added
private Task NavigateToLeads(DealershipDto dealership) { NavigationManager.NavigateTo($"/leads?DealershipId={dealership.Id}"); return Task.CompletedTask; }
Then in LeadsAppService.Extended.cs, I have
public async Task\<PagedResultDto> GetListByDealershipAsync(Guid dealershipId, PagedAndSortedResultRequestDto input)
{ var filterInput = new GetLeadsInput { DealershipId = dealershipId, Sorting = input.Sorting, MaxResultCount = input.MaxResultCount, SkipCount = input.SkipCount };
return await GetListAsync(filterInput);
}
This works and gives me a new button to add a lead filtered on the selected dealership.
I tried following the guide in this article but all I could get to work was add buttons not removing or editing.
#1: Is this the best way to do this or is there an obvious "ABP way" that I'm missing?
#2 If it is the best way, how do I disable/hide the default create button (New Lead below)?
#3 On create and edit, how do I hide/disable the Dealership selector as we don't want any chance of it being changed before saving.
The answer to #1 may make questions 2 and 3 moot. Thanks for any help.
John
Thanks.
If you Save and Generate a property with a default value, the value will be set on new entries as long as Required isn't checked. If Required is checked, the default value isn't populated when creating a new record. If you uncheck Required and Save and Generate, Default Value will again be set when creating a new record.