Search code examples
c#asp.net-corecontrollerasp.net-core-webapi

How to retain field value in between requests in C#?


I have the following Service:

public class MyService: IMyService
{
    private string myToken;

    public MyService()
    {
    }

    public void SetMyToken(string token)
    {
        Console.WriteLine($"This is the parameter token: {token}");
        myToken = token;
        Console.WriteLine($"This is myToken: {myToken}");
    }

    public async Task<string> TriggerPipeline()
    {
        Console.WriteLine($"Calling TriggerPipeline function. myToken: {myToken}");
        // the rest of the code
    }
}

I have the following Controller:

[Route("[controller]")]
[ApiController]
public class MyController: ControllerBase
{
    private readonly IMyService _myService;

    public MyController(IMyService myService,)
    {
        _myService = myService;
    }

    [HttpGet("set-token")]
    public async Task<IActionResult> SetToken(string token)
    {
        _myService.SetMyToken(token);
        return Ok("Token set successfully");
    }

    [HttpPost("trigger-pipeline")]
    public async Task<IActionResult> TriggerPipeline()
    {
        // the rest of the code
    }
}

In the Controller, when I call SetToken endpoint and pass in "123", and then followed by calling TriggerPipeline endpoint, I get this on the Console:

This is the parameter token: 123
This is myToken: 123
Calling TriggerPipeline function. myToken:

As you can see, after one Http call on SetToken, I am able to retain the token value ("123"). However, after another Http call on TriggerPipeline, I am no longer retaining that token.

In my Program.cs, I am already using Scoped Services:

builder.Services.AddScoped<IMyService, MyService>();

I can make it work by simply making myToken a static field. But in my case, I don't want to do that because whenever someone logs in, I want the token to be tied to that user. Can anyone help me on this issue?


Solution

  • Agree with Steve Py, the scoped service instance is created per request. To retain the value between requests, you could use a Singleton service if the value should persist across all requests throughout the application's lifecycle.

    The following are main difference between the service lifetime:

    • Transient: A new instance is created each time the service is requested.
    • Scoped: A new instance is created per request.
    • Singleton: A single instance is created and shared across the entire application

    But in my case, I don't want to do that because whenever someone logs in, I want the token to be tied to that user

    According to your requirement, since you want to set token based on users. You could use Session Storage to store the token.

    Try to modify your services as below:

    public interface IMyService
    {
        void SetMyToken(string token);
    
        Task<string> TriggerPipeline();
    }
    public class MyService : IMyService
    {
        private string myToken;
        private readonly IHttpContextAccessor _httpContextAccessor;
        public MyService(IHttpContextAccessor httpContextAccessor)
        {
            _httpContextAccessor = httpContextAccessor;
        }
    
        public void SetMyToken(string token)
        {
            Console.WriteLine($"This is the parameter token: {token}");
            myToken = token;
    
            var context = _httpContextAccessor.HttpContext;
    
            //check session exist.
            if (context?.Session != null)
            { 
                context.Session.SetString("token", token);
            }
    
            Console.WriteLine($"This is myToken: {myToken}");
        }
    
        public async Task<string> TriggerPipeline()
        {
            var context = _httpContextAccessor.HttpContext;
            if (context?.Session != null)
            { 
                myToken = context.Session.GetString("token");
            }
    
            Console.WriteLine($"Calling TriggerPipeline function. myToken: {myToken}");
            // the rest of the code
             
            return await Task.FromResult<string>(myToken);
        }
    }
    

    And register the session in Program.cs file:

    builder.Services.AddHttpContextAccessor();
    
    builder.Services.AddDistributedMemoryCache();
    builder.Services.AddSession(options =>
    {
        options.IdleTimeout = TimeSpan.FromMinutes(20);
        options.Cookie.HttpOnly = true;
        options.Cookie.IsEssential = true;
    });
    builder.Services.AddScoped<IMyService,MyService>();  
     
    ...
    
    app.UseSession();
    

    Then, when using different user set/get the token, the result as below:

    After user A set token, if user B directly access the token, the token is null, after user B set token, he can get token.

    output