Search code examples
c#asp.net-coredependency-injectionblazorblazor-server-side

Is there a way to refresh the DI object only (Blazor Server)


I have an issue with Blazor Server:

my DI always get the same ID (instance) on the component's button click

DI

For example: ILoginService.ID = "123"

When i click a button - if the login fails , and retry it will have the same instance ID ="123"

The dependency injection was defined below: DI Definition

I am aware that if I refresh the page, the DI will work as expected (because it is scope service - new every request) . but it's not fit on my scenario.

I have to refresh the DI object only before or after the button click, so i have a new instance each button click

Is there a way to refresh the DI object only

Already changed the lifetime of DI to transient. Still not working chnged

I was expecting the following

Before Click: ILoginService.ID = "123"

Then Login failed Retry Click ILoginService.ID = "456"


Solution

  • In Blazor Server, components may reuse the same instance of the component upon button clicks, which can affect the retrieval and management of service instances. Even if the service is transient, a new service instance might not be created due to component caching mechanisms.

    So, to ensure that a new LoginService instance is obtained with each button click, you can create a new scope by using IServiceScopeFactory.

    Below is my test demo:

    LoginService:

    public class LoginService : ILoginService
    {
        public string ID { get; private set; } = Guid.NewGuid().ToString();
        // ...
    }
    

    Login.razor:

    @page "/login"
    @rendermode InteractiveServer
    @inject IServiceScopeFactory ScopeFactory
    
    <h3>Test</h3>
    
    <button @onclick="HandleLogin">Login</button>
    <p> ID: @id</p>
    
    @code {
        private string id;
    
        private async Task HandleLogin()
        {
            using (var scope = ScopeFactory.CreateScope())
            {
                var loginService = scope.ServiceProvider.GetRequiredService<ILoginService>();
                id = loginService.ID;
            }
        }
    }
    

    Also register the service as:

    builder.Services.AddScoped<ILoginService, LoginService>();
    

    My test results:

    First click:

    First click

    Second click:

    Second click