Activities of "shodgson"

[maliming] said: hi

The ApplicationConfigurationDtoCacheAbsoluteExpiration of AbpAspNetCoreMvcClientCacheOptions is five minutes by default.

Can you try to change it to 10 seconds to test again?

Thanks.

Thanks for the response.

Where should I configure those options?

I have a blazor web app for the front-end.

I tried to;

    Configure<AbpAspNetCoreMvcClientCacheOptions>(options =>
    {
        options.ApplicationConfigurationDtoCacheAbsoluteExpiration = TimeSpan.FromSeconds(5);
    });

In the blazor server; the feature value in my microservice is still not updated after 5 seconds In the microservice that check the feature; same thing

Here's the simple snippet i'm using as an example in one of my service to condition on the feature value :

    public async Task<PagedResultDto<ResultDto>> GetResultsAsync(Guid id, GetResultsDto input)
    {
        // not updated based on the value set in the feature management from the host UI
        var isNewFlowEnabled = await featureChecker.IsEnabledAsync(AdministrationFeatures.NewFlow);

        if (isNewFlowEnabled)
        {
            return await GetNewResultsAsync(input);
        }
        else
        {
            return await GetLegacyResultsAsync(input);
        }
    }

I still need someone to provide a way to have the cache invalidated when updating features in the host so that I can confidently rely on the Feature Management module to configure feature flags for my application and deterministically know when those updates will be reflected in the application.

[maliming] said: hi

You can try to set command timeout to 60 seconds or more

public override void ConfigureServices(ServiceConfigurationContext context) 
{ 
    //... 
 
    Configure<AbpDbContextOptions>(options => 
    { 
        options.UseSqlServer(x => x.CommandTimeout(60)); 
    }); 
     
    //... 
} 

Hello,

I tried to set the command timeout to 2 minutes and I face the same issue regardless after a minute. After a minute the export-as-csv call fails and returns a 401 Unauthorized. Here's the stack trace I get:

[14:51:15 INF] Executing action method Volo.Abp.Identity.IdentityUserController.GetListAsCsvFileAsync (Volo.Abp.Identity.Pro.HttpApi) - Validation state: Valid
[14:51:15 WRN] ---------- RemoteServiceErrorInfo ----------
{
  "code": null,
  "message": "An internal error occurred during your request!",
  "details": null,
  "data": null,
  "validationErrors": null
}

[14:51:15 WRN] Invalid download token: 1d7c6dcbc0854168a1c9abc4a2323f23
Volo.Abp.Authorization.AbpAuthorizationException: Invalid download token: 1d7c6dcbc0854168a1c9abc4a2323f23
   at Volo.Abp.Identity.IdentityUserAppService.CheckDownloadTokenAsync(String token, Boolean isInvalidUsersToken)
   at Volo.Abp.Identity.IdentityUserAppService.GetExportUsersAsync(GetIdentityUserListAsFileInput input)
   at Volo.Abp.Identity.IdentityUserAppService.GetListAsCsvFileAsync(GetIdentityUserListAsFileInput input)
   at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
   at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
   at Volo.Abp.GlobalFeatures.GlobalFeatureInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
   at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
   at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
   at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
   at Volo.Abp.Authorization.AuthorizationInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
   at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
   at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
   at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
   at Volo.Abp.Auditing.AuditingInterceptor.ProceedByLoggingAsync(IAbpMethodInvocation invocation, AbpAuditingOptions options, IAuditingHelper auditingHelper, IAuditLogScope auditLogScope)
   at Volo.Abp.Auditing.AuditingInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
   at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
   at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
   at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
   at Volo.Abp.Validation.ValidationInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
   at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
   at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
   at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
   at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
   at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
   at lambda_method4261(Closure, Object)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.&lt;InvokeActionMethodAsync&gt;g__Logged|12_1(ControllerActionInvoker invoker)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.&lt;InvokeNextActionFilterAsync&gt;g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.&lt;InvokeInnerFilterAsync&gt;g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.&lt;InvokeNextExceptionFilterAsync&gt;g__Awaited|26_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
[14:51:15 WRN] Code:
[14:51:15 INF] AuthenticationScheme: Bearer was challenged.
[14:51:15 INF] Executed action Volo.Abp.Identity.IdentityUserController.GetListAsCsvFileAsync (Volo.Abp.Identity.Pro.HttpApi) in 68.2107ms
[14:51:15 INF] Executed endpoint 'Volo.Abp.Identity.IdentityUserController.GetListAsCsvFileAsync (Volo.Abp.Identity.Pro.HttpApi)'
[14:51:15 INF] AUDIT LOG: [401: GET    ] /api/identity/users/export-as-csv

hi

Can you try to override the ImportUsersFromFileAsync method of IdentityUserAppService to support batch import?

We will change it in the next version.

Thanks.

[Authorize(IdentityPermissions.Users.Import)] 
public virtual async Task<ImportUsersFromFileOutput> ImportUsersFromFileAsync(ImportUsersFromFileInputWithStream input) 
{ 
    await IdentityOptions.SetAsync(); 
 
    var stream = new MemoryStream(); 
    await input.File.GetStream().CopyToAsync(stream); 
 
    var invalidUsers = new List<InvalidImportUsersFromFileDto>(); 
    List<InvalidImportUsersFromFileDto> waitingImportUsers; 
    try 
    { 
        IConfiguration configuration = null; 
        if (input.FileType == ImportUsersFromFileType.Csv) 
        { 
            configuration = new CsvConfiguration { Seperator = ';' }; 
        } 
 
        waitingImportUsers = (await stream.QueryAsync<InvalidImportUsersFromFileDto>(excelType: input.FileType == ImportUsersFromFileType.Excel ? ExcelType.XLSX : ExcelType.CSV, configuration: configuration)).ToList(); 
    } 
    catch (Exception) 
    { 
        throw new BusinessException(IdentityProErrorCodes.InvalidImportFileFormat); 
    } 
 
    if (!waitingImportUsers.Any()) 
    { 
        throw new BusinessException(IdentityProErrorCodes.NoUserFoundInFile); 
    } 
 
    var resultDto = new ImportUsersFromFileOutput 
    { 
        AllCount = waitingImportUsers.Count 
    }; 
 
    const int batchSize = 3; 
    var totalUsers = waitingImportUsers.Count; 
    var batchCount = (int)Math.Ceiling((double)totalUsers / batchSize); 
    for (var batchIndex = 0; batchIndex < batchCount; batchIndex++) 
    { 
        var currentBatch = waitingImportUsers 
            .Skip(batchIndex * batchSize) 
            .Take(batchSize) 
            .ToList(); 
        var (invalidBatchUsers, successBatchUsers) = await ImportUsersAsync(currentBatch); 
        invalidUsers.AddRange(invalidBatchUsers); 
        if (invalidBatchUsers.Any() && successBatchUsers.Any()) 
        { 
            var (invalidBatchUsers2, successBatchUsers2) = await ImportUsersAsync(successBatchUsers); 
            if (invalidBatchUsers2.Any()) 
            { 
                invalidUsers.AddRange(invalidBatchUsers2); 
                invalidUsers.AddRange(successBatchUsers2); 
            } 
        } 
    } 
 
    if (invalidUsers.Any()) 
    { 
        var token = Guid.NewGuid().ToString("N"); 
 
        await ImportInvalidUsersCache.SetAsync( 
            token, 
            new ImportInvalidUsersCacheItem 
            { 
                Token = token, 
                InvalidUsers = invalidUsers, 
                FileType = input.FileType 
            }, 
            new DistributedCacheEntryOptions 
            { 
                AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(1) 
            }); 
 
        resultDto.InvalidUsersDownloadToken = token; 
    } 
 
    resultDto.SucceededCount = resultDto.AllCount - invalidUsers.Count; 
    resultDto.FailedCount = invalidUsers.Count; 
 
    return resultDto; 
} 
 
protected virtual async Task<(List<InvalidImportUsersFromFileDto>, List<InvalidImportUsersFromFileDto>)> ImportUsersAsync(List<InvalidImportUsersFromFileDto> users) 
{ 
    var hasException = false; 
    var invalidUsers = new List<InvalidImportUsersFromFileDto>(); 
    var successUsers = new List<InvalidImportUsersFromFileDto>(); 
    using (var uow = UnitOfWorkManager.Begin(requiresNew: true, isTransactional: true)) 
    { 
        foreach (var waitingImportUser in users) 
        { 
            try 
            { 
                var user = new IdentityUser( 
                    GuidGenerator.Create(), 
                    waitingImportUser.UserName, 
                    waitingImportUser.EmailAddress, 
                    CurrentTenant.Id 
                ) 
                { 
                    Surname = waitingImportUser.Surname, 
                    Name = waitingImportUser.Name 
                }; 
 
                if (!waitingImportUser.PhoneNumber.IsNullOrWhiteSpace()) 
                { 
                    user.SetPhoneNumber(waitingImportUser.PhoneNumber, false); 
                } 
 
                if (!waitingImportUser.Password.IsNullOrWhiteSpace()) 
                { 
                    (await UserManager.CreateAsync(user, waitingImportUser.Password)).CheckErrors(); 
                } 
                else 
                { 
                    (await UserManager.CreateAsync(user)).CheckErrors(); 
                } 
 
                if (!waitingImportUser.AssignedRoleNames.IsNullOrWhiteSpace()) 
                { 
                    (await UserManager.SetRolesAsync(user, 
                        waitingImportUser.AssignedRoleNames.Split(new[] { ",", ";" }, 
                            StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries))).CheckErrors(); 
                } 
 
                if (!waitingImportUser.AssignedOrganizationUnitNames.IsNullOrWhiteSpace()) 
                { 
                    var ouNames = waitingImportUser.AssignedOrganizationUnitNames.Split(new[] { ",", ";" }, 
                            StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries).ToArray(); 
                    var ous = await OrganizationUnitRepository.GetListByDisplayNamesAsync(ouNames); 
                    if (ous.Any()) 
                    { 
                        await UserManager.SetOrganizationUnitsAsync(user, ous.Select(x => x.Id).ToArray()); 
                    } 
                } 
 
                successUsers.Add(waitingImportUser); 
            } 
            catch (Exception e) 
            { 
                hasException = true; 
 
                waitingImportUser.ErrorReason = e is UserFriendlyException ? e.Message : e.ToString(); 
                invalidUsers.Add(waitingImportUser); 
                Logger.LogWarning(e, $"Import user failed: {waitingImportUser}"); 
            } 
        } 
 
        await (hasException ? uow.RollbackAsync() : uow.CompleteAsync()); 
    } 
 
    return (invalidUsers, successUsers); 
} 
 

Thanks for the update. I'll try that.

Any workaround for the Export as well?

I had to create a new script that set the abp.appPath to my pathBase cause that's what abp uses internally to call the auth server in the account-proxy.js. It's working now.

account-proxy.js :

context.Services.AddControllers(options => { options.Conventions.Insert(0, new GlobalRouteReplaceConvention("api", "auth/api")); });

I don't think replacing the api controller convention will work in my case.

It's the actual url used to make the change-password request that is not accounting for the path base. I did try to change the conventions based on your response and the URL used internally is still the same as before.

Where is that URL taken from and how can I override it to include my base path?

Hey, thanks for the response.

Unless i'm mistaken, the auth server requests targeting the /api/account/my-profile within the Account/Manage page does not go through the web gateway.

Locally, when running everything as default and without any custom base path for my auth server, I do see all /api/account/my-profile requests reaching the auth server directly without going through the web gateway. I can repro the same behavior when starting from a new microservice template.

Repro steps:

  • Create a new microservice solution with a Blazor web app
  • Go on the /Account/Manage page hosted on the auth server
  • Change password and submit
  • Notice that the /api/account requests are directly sent to the auth server and not the web gateway

Hi,

Thanks to the information you provided, I was able to reproduce the problem. The problem does not occur in the Application Layered template, but in the Microservice solution, so I could not reproduce it the first time. I will create an internal issue, and we will keep you informed about the progress. Thank you for your patience.

Thank you,

I'll wait for an issue link to track the progress

Thanks for the response.

Unfortunately it seems I can reproduce the issue even on a new microservice solution.

I'm on version 9.1

Steps to reproduce on my side:

  • Create a new microservice solution from the abp studio
  • EF Core database provider
  • Multitenancy enabled
  • Blazor WebApp as the UI framework
  • No mobile framework
  • No public website
  • No dynamic localization
  • OpenIddict UI enabled
  • LeptonX theme
  • Add this block in the .Client assembly in the MenuContributor file
 var currentTenant = context.ServiceProvider.GetRequiredService<ICurrentTenant>();

 Console.WriteLine($"Running on the browser: {OperatingSystem.IsBrowser()} Current tenant is {currentTenant.Id}: {currentTenant.Name}");
  • Run the solution and create a tenant
  • Login on that new tenant and notice the empty tenantid/tenantname in the console
Showing 11 to 19 of 19 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.1.0-preview. Updated on October 30, 2025, 06:33