Blazor Server: It is possible get Current Loged User In DbContext

Jan Mucha 101 Reputation points
2021-03-17T08:54:54.227+00:00

Hello, I build blazor server multi tenant application and I wants to use HasQueryFilter in entity framework DbContext for predefined filter with TenantId. But I cannot get Current Loged User in DbContext. I inject to DB Context AuthenticationStateProvider but I get error GetAuthenticationStateAsync was called before SetAuthenticationState.

It is possible use HasQueryFilter depend on Current Logged User in DB Context?

Entity Framework Core
Entity Framework Core
A lightweight, extensible, open-source, and cross-platform version of the Entity Framework data access technology.
696 questions
Blazor
Blazor
A free and open-source web framework that enables developers to create web apps using C# and HTML being developed by Microsoft.
1,395 questions
{count} votes

Accepted answer
  1. Jan Mucha 101 Reputation points
    2021-03-20T16:18:23.767+00:00

    Hello, finally I found solution! From my view it is bug! Problem is because services.AddDbContextFactory is registered as Singleton. I create my own implementation of IDbContext factory and register it as Scoped. After this change everything’s works perfect. When I change registration scope of DbContextFactory to singleton, I get the error: GetAuthenticationStateAsync was called before SetAuthenticationState.

    MyDbContextFactory:

    public class BlazorContextFactory<TContext> : IDbContextFactory<TContext> where TContext : DbContext
        {
            private readonly IServiceProvider provider;
    
            public BlazorContextFactory(IServiceProvider provider)
            {
                this.provider = provider;
            }
    
            public TContext CreateDbContext()
            {
                if (provider == null)
                {
                    throw new InvalidOperationException(
                        $"You must configure an instance of IServiceProvider");
                }
    
                return ActivatorUtilities.CreateInstance<TContext>(provider);
            }
        }
    

    MyStartUp:

    public class Startup
        {
            public Startup(IConfiguration configuration)
            {
                Configuration = configuration;
            }
    
            public IConfiguration Configuration { get; }
    
            // This method gets called by the runtime. Use this method to add services to the container.
            // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddScoped<ApplicationDbContext>();
                services.AddScoped<IDbContextFactory<ApplicationDbContext>, BlazorContextFactory<ApplicationDbContext>>();
    
                services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
                    .AddEntityFrameworkStores<ApplicationDbContext>();
    
                services.AddRazorPages();
                services.AddServerSideBlazor();
                services.AddScoped<AuthenticationStateProvider, RevalidatingIdentityAuthenticationStateProvider<IdentityUser>>();
                services.AddSingleton<WeatherForecastService>();
            }
    
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                    app.UseDatabaseErrorPage();
                }
                else
                {
                    app.UseExceptionHandler("/Error");
                    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                    app.UseHsts();
                }
    
                app.UseHttpsRedirection();
                app.UseStaticFiles();
    
                app.UseRouting();
    
                app.UseAuthentication();
                app.UseAuthorization();
    
                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapControllers();
                    endpoints.MapBlazorHub();
                    endpoints.MapFallbackToPage("/_Host");
                });
            }
    

    I hope it's help other peoples!


2 additional answers

Sort by: Most helpful
  1. Jan Mucha 101 Reputation points
    2021-03-19T08:28:57.837+00:00

    Hello, i do some changes in demo solution and now is pretty clear where I have problem. I fix problem with DbContext and DbContext Factory Registration. I'm injecting AuthenticationStateProvider into ApplicationDbContext and whne I use @inject ApplicationDbContext DbContext; it works very well (/Counter page).
    If I use @inject IDbContextFactory<ApplicationDbContext> Factory; there is error: Cannot resolve scoped service 'Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider' from root provider. (/CounterFail page). ​I hope it wil be more understable for you.
    Edited solution is there.

    Succes result on page counter
    79527-image.png

    Error on page counterfail
    79612-image.png

    1 person found this answer helpful.

  2. Jan Mucha 101 Reputation points
    2021-03-18T09:53:39.607+00:00

    Hello, I have only one DbContext in application. Tenant base entity, has TenantId and base on logged user there is implementation of Global Query Filters. In DbContext I need now current logged user and base on user I get ActiveTenatnId and set up query filter.

    I prepare small demo project you can download there. In this project is counter page. with Click me button. When you click on the button it call SaveChangeAsync Method. Insight in the method is request for current user name and it works! I'm thing, this approach is not gut. In .net core 3.1 version I have problem with DbContext: A second operation started on this context before a previous operation completed.
    79110-image.png

    In the second scenario I'm using DbContextFactory. For simulate this scenario in demo project you have to comment and uncomment some code in startup.cz like this:
    79192-image.png
    But as you can see there is exception.

    I have realy easy requirement. I need get current logged user id in application. In my project is most of other logic which require current logged user.

    Can you help me please I'm hopeless. I spend 5 days with this problem and I haven't any results.

    0 comments No comments