Hello,
In my scenario, i need to defined dynamically the connection string based on the user.
In order to do that i have a CustomLogonController. Where in insert the CS in a the Value Manager.
This seems to work.
C#public class CustomLogonParametersCtrl : ObjectViewController<DetailView, AuthenticationStandardLogonParameters> {
public CustomLogonParametersCtrl() : base() {
// Target required Views (use the TargetXXX properties) and create their Actions.
}
protected override void OnActivated() {
base.OnActivated();
LogonController controller = Frame.GetController<LogonController>();
if(controller != null) {
controller.Actions["Logon"].Executing += CustomLogonParametersCtrl_Executing;
}
}
private void CustomLogonParametersCtrl_Executing(object sender, System.ComponentModel.CancelEventArgs e) {
BlazorApplication app = ((BlazorApplication)Application);
IValueManagerStorageContext valueManagerStorageContext = app.ServiceProvider.GetRequiredService<IValueManagerStorageContext>();
valueManagerStorageContext.RunWithStorage(() => {
ValueManager.GetValueManager<string>("ApplicationCS").Value = "Integrated Security=SSPI;Pooling=false;Data Source=(localdb)\\mssqllocaldb;Initial Catalog=XAfTest0034";
});
}
}
Then on the Blazor application in the OnLoggingOn, I get the CS.
C#protected override void OnLoggingOn(LogonEventArgs args)
{
base.OnLoggingOn(args);
Tracing.Tracer.LogText("Going into OnLoggingOn");
try
{
var username = ((AuthenticationStandardLogonParameters)args.LogonParameters).UserName;
string cs = string.Empty;
IValueManagerStorageContext valueManagerStorageContext = ServiceProvider.GetRequiredService<IValueManagerStorageContext>();
valueManagerStorageContext.RunWithStorage(() => {
cs = ValueManager.GetValueManager<string>("ApplicationCS").Value;
Tracing.Tracer.LogText($"The CS from value manager is not empty : {cs}");
if (string.IsNullOrEmpty(cs))
{
Tracing.Tracer.LogText($"The CS from value manager is empty, setting value... : {cs}");
ValueManager.GetValueManager<string>("ApplicationCS").Value = "Integrated Security=SSPI;Pooling=false;Data Source=(localdb)\\mssqllocaldb;Initial Catalog=XAfTest0034";
}
});
((XPObjectSpaceProvider)ObjectSpaceProviders[0]).SetDataStoreProvider(GetDataStoreProvider(cs, null));
}
catch (Exception ex)
{
throw new UserFriendlyException($"Nom d'utilsateur ou mot de passe inconnu. Veuillez vérifier les données entrées ou appeler le support technique.");
}
}
When running, I see that the first time going in into the OnLoggingOn, I can get the CS from the value manager. But the app goes a second time into the OnLogginOn and the ValueManager provides a null value.
Can you help on why the second time it has a null value ?
Many thanks
ISA
HI ISA,
Currently, I'm not sure if it's possible to use ValueManager in XAF Blazor to store information during logging in. In any case, please give us some time to research if it's possible to make it work.
Meanwhile, from the following ticket, I see that you previously stored the user connection string in a database: Dynamic Database Name XAF Blazor. Would you please clarify why you decided to switch from this solution and where you are going to obtain the user connection string now?
Just to put some context on this ticket :
We are trying to Implement a variation of E1344. This is where we took and use the following methods in the XpoDataStoreProviderAccessor class :
public IXpoDataStoreProvider GetDataStoreProvider(string connectionString, IDbConnection connection) { return xpoDataStoreProviderDictionary.GetOrAdd(connectionString, CreateDataStoreProvider); IXpoDataStoreProvider CreateDataStoreProvider(string connectionString) { return XPObjectSpaceProvider.GetDataStoreProvider(connectionString, connection, false); } }
In the E1344, the connection string is built based on the constant value.
I our case, the username give the CS. In detail, we query an external DB to get the CS based on the username.
In order to do that, we are trying the store in the memory the CS after the user clicks on login.
In the first version, we requested the DB on the OnLoggingOn but this was a bad idea as modules like report and dashboard go in this method several times. This opens many sessions in the db.
Then we also created a class that we added in startup :
public void ConfigureServices(IServiceCollection services) { services.AddScoped<TenantInfo>(); //.... }
The behaviour is the same. the first time we value was in there and the second time it was set as null.
Hi Gosha,
Just finished my message and did not see you answered.
When using the report wizard / designer or dashboard, this solution goes in the OnLoggingOn dozens times.
This generates many connections that are not directly closed. What is strange is that in a normal way (this cs in the appsetting.json), i have only around 5 open sessions in the pool.
For example, if you run the attached sample and run a dashboard designer, we will see that my tracing messages in the OnlogginOn will appear around 20 times just by starting the dashboard designer and adding a grid to it.
Maybe I should go in a simpler way here and just put static string property that I assign in OnLoggingOn once.
The idea is to keep this value with the CS until the user logoff.
public static string TenantCS; protected override void OnLoggingOn(LogonEventArgs args) { base.OnLoggingOn(args); try { Tracing.Tracer.LogText("Going into OnLoggingOn"); IConfiguration configuration = ServiceProvider.GetRequiredService<IConfiguration>(); var adminCS = configuration.GetConnectionString("AdminCS"); var username = ((AuthenticationStandardLogonParameters)args.LogonParameters).UserName; if(string.IsNullOrEmpty(TenantCS)) { TenantCS = BrokeroAdminHelper.GetTenantCSFromNpgsqlAsync(adminCS, username); } ((XPObjectSpaceProvider)ObjectSpaceProviders[0]).SetDataStoreProvider(GetDataStoreProvider(TenantCS, null)); } catch(Exception ex) { throw new UserFriendlyException($"Nom d'utilsateur ou mot de passe inconnu. Veuillez vérifier les données entrées ou appeler le support technique."); } }
Do you see any possible issue doing this ?