'Use NETCore ScopedService outside Controller

First, let me explain breafly what I have, what I want to achieve, how I did it so far, and why I'd like to improve my current implementation.


WHAT I HAVE

Basically, I have a .NET Core project that runs an API Service with some APIs.
Also, I have a class called MyFundamentalClass, which is used throughout the whole application: in fact, MyFundamentalClass implements the Singleton Pattern, having something like this:

public class MyFundamentalClass {
    private static _myFundamentalClass = null;

    public static MyFundamentalClass GetInstance() {
        if (_myFundamentalClass == null)
            _myFundamentalClass = new MyFundametalClass();

        return _myFundamentalClass;
    }
}

Reason why I want this Singleton Pattern is that this class is used in many occasion in the whole project; the alternative would be to instantiate the class in the ControllerAction, and then pass it basically EVERYWHERE: it's not sustainable.


THE PROBLEM

As you can imagine, here was the first problem: each request MUST HAVE its own instance of MyFundamentalClass. As you can imagine, static keyword does not work very well with that.

Why I'm telling this: if I want an instance of MyFundamentalClass in each ControllerAction, I should write something like this:

public async Task<ActionResult> GetUserData() {
    MyFundamentalClass = new MyFundamentalClass();
    return await MyFundametalClass.GetUserData();
}

So far so good, but as I said, I need the Singleton Pattern, so I should change the code into:

public async Task<ActionResult> GetUserData() {
    MyFundamentalClass = MyFundamentalClass.GetInstance();
    return await MyFundametalClass.GetUserData();
}

What's the problem? Two different API calls will overwrite the private field MyFundamentalClass._myFundamentalClass, mixing the context of the two API. HUGE PROBLEM!


MY CURRENT SOLUTION

What I found , the only way, was the use of AsyncLocal<MyFundamentalClass>. I've something like this:

public class RequestContext {
    publicv static AsyncLocal<MyFundamentalClass> Instance = new AsyncLocal<MyFundamentalClass>(null);
}

// Then, in each ControllerAction
public async Task<ActionResult> GetUserData() {
    var method = async () => {
        RequestContext.Instance.Value = new MyFundamentalClass();
        // Whatever I need to do 
    }
}

// Then, in the MyFundamentalClass
public MyFundamentalClass {
    public MyFundamentalClass GetInstance() {
        return RequestContext.Instance.Value;
    }
}

With this solution, since the AsyncLocal context lives only thourghout the async context, it perfectly fits my need.
Though, why am I searching for something else? Because I feel like I am missusing the Dependency Injection and the whole ServiceProvider stuffs.


WHAT I AM LOOKING FOR

So.. I also come upon the services.AddScoped<MyFundamentalClass>() code (Link to Microsfot DOC).

By what it tells, it should perfectly fit my need: what is created by that AddScoped lives only for the API -> one API one instance.

But, problem is: how could I exploit the instance created by AddScoped with the Singleton Pattern?
I know that, with DependencyInject, in my Controller I can add the object in the constructor:

public class MyController : ControllerBase {
    private MyFundamentalClass _myFundamentalClass;

    public MyController(MyFundamentalClass myFundamentalClass) {
        _myFundamentalClass = myFundamentalClass;
    }

    public async Task<ActionResult> GetUserData() {
        return await _myFundamentalClass.GetUserData();
    }
}

That feels much more correct, from a code point of view, but.. I don't have the SingletonPattern anymore, unless I still use the AsyncContext.

What I thought it was possible was to use:

public static IServiceProvider ServiceProvider;

public static WorkbenchViewModel GetInstance() {
    ServiceProvider.GetService(typeof(WorkbenchViewModel));
}

But I have the same problem: each request has its own IServiceProvider, thus different API would override the value.



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source