Hi team,
I'm currently working on a multi-tenant application using the ABP Framework and I would like to clarify something regarding background processing.
Is IBackgroundWorker tenant-safe by default? In other words, when using background workers in a multi-tenant application, does ABP ensure that each tenant has its own isolated instance or context when the worker is running?
If not, what is the recommended or best practice to trigger a BackgroundJob for each tenant from within a BackgroundWorker? I'm looking for a safe and scalable way to ensure tenant-specific background processing.
Would an approach like the one below be correct?
protected override async Task DoWorkAsync(PeriodicBackgroundWorkerContext workerContext)
{
var currentTenant = workerContext.ServiceProvider.GetRequiredService<ICurrentTenant>();
var tenantStore = workerContext.ServiceProvider.GetRequiredService<ITenantStore>();
var tenants = await tenantStore.GetListAsync();
foreach (var tenant in tenants)
{
if (tenant.IsActive)
{
using (currentTenant.Change(tenant.Id))
using (var scope = workerContext.ServiceProvider.CreateScope())
{
}
}
else
{
}
}
}
Any guidance or examples would be greatly appreciated.
Thanks in advance!
Ok I see, I didn't realize that endpoint was there. I guess I could use that. Thanks for your help.
Because I am sending a request from the API to change the settings. (/api/account-admin/settings/external-provider)
Hi, Thanks for your help, it worked as expected.
Hi, I didn't add it to the api project as you said. When I added the first codes I wrote to the api project, it worked without any problems. Thanks for your help.
Hi,
I modified my code as shown below.
context.Services.AddAuthentication()
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, OpenIdConnectDefaults.AuthenticationScheme, options =>
{
})
.WithDynamicOptions<OpenIdConnectOptions, OpenIdConnectHandler>(
OpenIdConnectDefaults.AuthenticationScheme,
options =>
{
options.WithProperty(x => x.ClientId);
options.WithProperty(x => x.ClientSecret, isSecret: true);
}
);
And I tried to update the settings configuration with the following data.
{
"verifyPasswordDuringExternalLogin": false,
"externalProviders": [
{
"name": "Google",
"enabled": false,
"properties": [
{
"name": "ClientId",
"value": null
}
],
"secretProperties": [
{
"name": "ClientSecret",
"value": null
}
]
},
{
"name": "Microsoft",
"enabled": false,
"properties": [
{
"name": "ClientId",
"value": null
}
],
"secretProperties": [
{
"name": "ClientSecret",
"value": null
}
]
},
{
"name": "Twitter",
"enabled": false,
"properties": [
{
"name": "ConsumerKey",
"value": null
}
],
"secretProperties": [
{
"name": "ConsumerSecret",
"value": null
}
]
},
{
"name": "OpenIdConnect",
"enabled": false,
"properties": [
{
"name": "ClientId",
"value": null
}
],
"secretProperties": [
{
"name": "ClientSecret",
"value": null
}
]
}
]
}
And same error
External provider with OpenIdConnect not definition!
System.Exception: External provider with OpenIdConnect not definition!
at Volo.Abp.Account.ExternalProviders.ExternalProviderSettingsHelper.SetAsync(ExternalProviderSettings settings)
at Volo.Abp.Account.AccountSettingsAppService.UpdateExternalProviderAsync(AccountExternalProviderSettingsDto input)
at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous(IInvocation invocation, IInvocationProceedInfo proceedInfo)
at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapter.ProceedAsync()
at Volo.Abp.Authorization.AuthorizationInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync(IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous(IInvocation invocation, IInvocationProceedInfo proceedInfo)
at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapter.ProceedAsync()
at Volo.Abp.GlobalFeatures.GlobalFeatureInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync(IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous(IInvocation invocation, IInvocationProceedInfo proceedInfo)
at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapter.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(IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous(IInvocation invocation, IInvocationProceedInfo proceedInfo)
at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapter.ProceedAsync()
at Volo.Abp.Validation.ValidationInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync(IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous(IInvocation invocation, IInvocationProceedInfo proceedInfo)
at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapter.ProceedAsync()
at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync(IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
at Volo.Abp.Account.AccountSettingsController.UpdateExternalProviderAsync(AccountExternalProviderSettingsDto input)
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Logged|12_1(ControllerActionInvoker invoker)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>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.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|26_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
Hello,
I would like to add a new provider in addition to the existing ExternalProviders you currently support. The code I’m using is as follows:
Configure<AbpExternalProviderOptions>(options =>
{
options.Definitions.Add(new ExternalProviderDefinition()
{
Name = OpenIdConnectDefaults.AuthenticationScheme,
Properties = new System.Collections.Generic.List<ExternalProviderDefinitionProperty>()
{
new ExternalProviderDefinitionProperty()
{
PropertyName = "ClientId"
},
new ExternalProviderDefinitionProperty()
{
PropertyName = "ClientSecret",
IsSecret = true
},
new ExternalProviderDefinitionProperty()
{
PropertyName = "Authority"
}
}
});
});
When I try to add a record for the new provider via the API (/api/account-admin/settings/external-provider), I receive the following error:
[00:44:30 ERR] External provider with OpenIdConnect not definition!
System.Exception: External provider with OpenIdConnect not definition!
at Volo.Abp.Account.ExternalProviders.ExternalProviderSettingsHelper.SetAsync(ExternalProviderSettings settings)
at Volo.Abp.Account.AccountSettingsAppService.UpdateExternalProviderAsync(AccountExternalProviderSettingsDto input)
at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous(IInvocation invocation, IInvocationProceedInfo proceedInfo)
at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapter.ProceedAsync()
at Volo.Abp.Authorization.AuthorizationInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync(IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous(IInvocation invocation, IInvocationProceedInfo proceedInfo)
at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapter.ProceedAsync()
at Volo.Abp.GlobalFeatures.GlobalFeatureInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync(IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous(IInvocation invocation, IInvocationProceedInfo proceedInfo)
at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapter.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(IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous(IInvocation invocation, IInvocationProceedInfo proceedInfo)
at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapter.ProceedAsync()
at Volo.Abp.Validation.ValidationInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync(IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous(IInvocation invocation, IInvocationProceedInfo proceedInfo)
at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapter.ProceedAsync()
at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync(IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
at Volo.Abp.Account.AccountSettingsController.UpdateExternalProviderAsync(AccountExternalProviderSettingsDto input)
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Logged|12_1(ControllerActionInvoker invoker)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>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.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|26_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
Is it supported to add a new provider this way? Thanks.
Thanks for the comment.
Hi,
I’m trying to make the ExternalProviders and VerifyPasswordDuringExternalLogin settings in the Account module tenant-specific by setting the Providers property to "T". To achieve this, I’ve created a custom SettingProvider class that inherits from SettingDefinitionProvider, as described in the documentation.
Below is the relevant code snippet from the Define method where I attempt to modify these settings:
public override void Define(ISettingDefinitionContext context)
{
var accountExternalProviders = context.GetOrNull(AccountSettingNames.ExternalProviders);
if (accountExternalProviders != null)
{
accountExternalProviders.Providers.Clear();
accountExternalProviders.Providers.Add("T");
}
var verifyPasswordDuringExternalLogin = context.GetOrNull(AccountSettingNames.VerifyPasswordDuringExternalLogin);
if (verifyPasswordDuringExternalLogin != null)
{
verifyPasswordDuringExternalLogin.Providers.Clear();
verifyPasswordDuringExternalLogin.Providers.Add("T");
}
//Define your own settings here. Example:
//context.Add(new SettingDefinition(SuiteSettings.MySetting1));
}
However, I’m encountering an issue: when the application is initialized for the first time, the Define method is called and works correctly — the related settings in the SettingDefinition table are updated as expected. But when the Define method is called again, those settings are no longer present in the context, so I cannot apply the changes, and the previously updated Providers value is set to null in the database.
Could you please help me understand the reason behind this behavior and suggest a solution?
Thank you.
Hi,
In our project, we use the AuthServer provided by ABP (configured as an MVC and decoupled project) for user authentication. After a successful login, the user is redirected to our custom-developed UI application. This UI consists of web and mobile clients, which communicate with the backend developed using ABP’s infrastructure (ApiHost).
What would be the best approach to transfer the language selected by the user during login on the AuthServer to both the UI (web and mobile) and the ApiHost layers?
Is there an ABP-recommended method or best practice to achieve this? The goal is to maintain the user's language preference consistently across all layers of the application (UI and API).
The diagram I added may help:
Thank you in advance for your help!