2

After learning about scoped/singleton/transient services here, I'm trying to understand why this code doesn't work. It's written in ASP.NET Core 2.

startup.cs

public void ConfigureServices(IServiceCollection services)
{
    // ... other services
    services.AddScoped<LiveData, LiveData>();
    services.AddScoped<Utils, Utils>();
}

livedata.cs

public class LiveData 
{
    public LiveData() {
        Plcs = new List<Plc>();
    }

    public List<Plc> Plcs { get; set; }
}

public class Plc
{
    public string Name { get; set; }
    public DateTime Datetime { get; set; }
    public string OperatorName { get; set; }
}

utils.cs

public class Utils
{
    private readonly LiveData _liveData;

    public Utils(LiveData liveData)
    {
        _liveData = liveData;
        // ... fill in _liveData.Plcs
    }

    public void SetOperatorName(string name, string operatorName)
    {
        Plc plc = _liveData.Plcs.SingleOrDefault(x => x.Name == name);
        plc.OperatorName = operatorName;
    }
}

testpage.chstml.cs

public class MachineDetailsModel : PageModel
{
    private readonly LiveData _liveData;
    private readonly Utils _utils;

    public MachineDetailsModel(LiveData liveData, Utils utils)
    {
        _liveData = liveData;
        _utils = utils;
    }

    public Plc Plc { get; set; }

    public async Task<IActionResult> OnGetAsync(string id)
    {
        Plc = _liveData.Plcs.SingleOrDefault(x => x.Name == id);
        return Page();
    }

    public IActionResult OnPostSetOperatorName(string name)
    {
        var operatorName = "foo";
        _utils.SetOperatorName(name, operatorName);
        Plc = _liveData.Plcs.SingleOrDefault(x => x.Name == name);
        return RedirectToPage();
    }
}

When the post handler is called, it correctly updates the OperatorName fields. Before returning I checked the Plc variable and it contains the new value "foo". But when the page reloads (due to the post) and the Plc variable is set again (note the two lines are the same) the OperatorName is lost (null, as default).

As far as I understand the scoped services maintain the same instance for all requests in the same session (i.e. until I close the browser). Did I understand it wrong?

Mark
  • 4,338
  • 7
  • 58
  • 120
  • 6
    `Did I understand it wrong?` Yes. The scope is the web request, not the 'session'. – mjwills Nov 29 '17 at 10:22
  • Ok. I definitely need a singleton one here... – Mark Nov 29 '17 at 10:33
  • Well then you need to register it as singleton rather than scoped. Note that singleton means 'one object shared by all web requests', not 'one object per session'. – mjwills Nov 29 '17 at 10:34
  • Does a singleton service "survive" on different "sessions"? I mean, when I close and reopen the browser, I will retrieve the same instance? – Mark Nov 29 '17 at 10:34
  • Yes. https://stackoverflow.com/questions/2155688/what-is-a-singleton-in-c – mjwills Nov 29 '17 at 10:35
  • Only one instance of a singleton service is ever created *per server instance*. Remember if you run in a web farm, there will be multiple instances on the different servers. You *may* need a database here :) – juunas Nov 29 '17 at 10:54
  • Correct @juunas - a singleton is per process. – mjwills Nov 29 '17 at 11:03
  • 2
    You shouldn't just jump to "well, I'll just use singleton-scope instead". Anything in singleton scope needs to be 100% thread-safe. If you're doing stuff like manipulating lists, newing up instances of things, etc. you're most likely going to run into issues in a multithreaded environment like a web server. In other words, if you need to persist data between requests, use an actual persistent store, like a database. Singletons are rarely the right approach. – Chris Pratt Nov 29 '17 at 14:42

0 Answers0