1

My question is about extending this previous post using identity to calculate the connection string for each user: ASP.NET Core change EF connection string when user logs in

I tried the following approach :

Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    var c = new SqlConnectionStringBuilder
    {
        -- the connection string to the users repository --
    };

    services.AddDbContextFactory<MasterDBContext>(options => 
        options.UseSqlServer(c.ConnectionString));

    services.AddScoped<MasterDBContext>(p => p.GetRequiredService<IDbContextFactory<MasterDBContext>>().CreateDbContext());

    services.AddDefaultIdentity<MyUser>(options => 
        options.SignIn.RequireConfirmedAccount = true) 
            .AddEntityFrameworkStores<MasterDBContext>();

    services.AddTransient<IMasterUserService, MasterUserService>();

    services.AddDbContextFactory<UserDbContext>();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapDefaultControllerRoute();
        endpoints.MapControllers();

        endpoints.MapBlazorHub();
        endpoints.MapFallbackToPage("/_Host");
    });
}

UserDbContext:

public MyContext(IServiceProvider provider)
{
      _provider = provider;
}

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
      var haccess = (IHttpContextAccessor)_provider.GetService(typeof(IHttpContextAccessor));
      var scopefactory = haccess.HttpContext.RequestServices.GetService<IServiceScopeFactory>();
      using (var scope = scopefactory.CreateScope())
      {
           var userManager = scope.ServiceProvider.GetRequiredService<UserManager<MyUser>>();
           var user = userManager.GetUserAsync(haccess.HttpContext.User).Result;
           var userServ = scope.ServiceProvider.GetRequiredService<IMasterUserService>();
           optionsBuilder.UseSqlServer(userServ.GetConnectionString(user).Result);
      }

      base.OnConfiguring(optionsBuilder);
}

But, even in a scope, no way to get access to UserManager service (usermanager injection works fine from others services and controllers). I get an "invalid operation exception" at the usermanager connection point.

What is wrong with that code ?

Thanks in advance

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
jmR
  • 11
  • 2
  • Like this: https://stackoverflow.com/questions/66842304/net-5-change-dbcontext-in-controller/66842794#66842794 – David Browne - Microsoft Apr 13 '21 at 20:30
  • Thanks David. I suppose i will go to something like this if i find no way to call UserManager service from the OnConfiguring method. Anyway, I don't understand why i'm not able to get access to UserManager with my code. – jmR Apr 15 '21 at 07:40
  • I don't either, but your code looks over-complicated. You should be able to simply inject the services you need in your DbContext constructor. – David Browne - Microsoft Apr 15 '21 at 15:27

1 Answers1

0

I found the solution at the end... My code in MyContext.OnConfiguring is correct if you add services.TryAddScoped<UserManager>(); in the ConfigureServices function of statup.cs.

All together, I'm able to get a connection string depending of the current user from any service :

public class MyService : IMyService { private IDbContextFactory _dbfactory;

public MyService(IDbContextFactory<MyContext> dbfactory)
{
    _dbfactory = dbfactory;
}

public async Task AnyAsync()
{
    using (var dbtf = _dbfactory.CreateDbContext())
    {
        ... your code ...
    }
}

}

jmR
  • 11
  • 2