1

i want to inject the AuthenticationStateProvider into the DatabaseContext. My Code looks like that:

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<AuthenticationStateProvider, RevalidatingIdentityAuthenticationStateProvider<IdentityUser>>();

    services.AddDbContextFactory<DatabaseContext>(options =>
    {
        options.UseSqlServer(Configuration.GetConnectionString("MyCon"));
        options.EnableSensitiveDataLogging();
    });

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

    services.AddIdentity<User, Role>()
        .AddEntityFrameworkStores<DatabaseContext>()
        .AddDefaultTokenProviders()
        .AddErrorDescriber<MultilanguageIdentityErrorDescriber>();

}

public DatabaseContext : IdentityDbContext
{
    private readonly AuthenticationStateProvider _authenticationStateProvider;

    public DatabaseContext(DbContextOptions options, AuthenticationStateProvider authenticationStateProvider)
    {
        _authenticationStateProvider = authenticationStateProvider;
    }
}

As soon as i start the App I run into the following error:

InvalidOperationException: Cannot resolve scoped service 'Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider' from root provider. in Startup.cs

{ options.UseSqlServer(Configuration.GetConnectionString("MyCon"));
  options.EnableSensitiveDataLogging();
});
services.AddScoped<DatabaseContext>(p =>
    p.GetRequiredService<IDbContextFactory<DatabaseContext>>()
    .CreateDbContext());

What i am doing wrong?

Thanks for your help!

JoeGER94
  • 177
  • 3
  • 14

1 Answers1

0

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.

My DbContextFactory

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);
        }
    }

My StartUp

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");
            });
        }
    }
Jan Mucha
  • 80
  • 9
  • Thanks for your answer but i think this is a really bad solution. Don't get me wrong but you're killing the benefit of the IDbContextFactory. I think you will run into Concurrency Problems which was the IDbContextFactory was made for to solve. The standard "DbContext" AddDbContext() is already register the DbContext as a scoped service. But please convince me! – JoeGER94 Mar 22 '21 at 13:54
  • Hello, I have realy big Blazor application with lots of logic before call SaveChanges, and everything works perfect! I thing there is bug in .net core framework, and this solution is only workaround. I hope this bug will be fixed sun. – Jan Mucha Mar 24 '21 at 15:45