awaiting the initialisation of a scoped property

Viewed 67

In a .net core application, I have an ISessionService (it just contains various reusable properties, like the current "Company" based on what the currently URL is.

It is defined in startup like this:

services.AddScoped<ISessionService, SessionService>();

SessionService has a property that is .Company. But in .Company a call is made to the database which, because it's a property, can't be awaited. So it currently looks like

public ICompany Company => dbContext.GetCompany(host).Result

The .Result is causing application hangs under heavy load. I could put this in the constructor of SessionService, but that just moves the .Result from the property to the constructor and causes the same problem.

Is there a way, through the initialisation of SessionService perhaps, to initialise in an async way without having to do any synchronous calls like .Result?

2 Answers

Do not introduce any synchronous blocking of asynchronous code. You'll hurt the performance and scalability of your application.

What you want should be done with a combination of a middleware and a feature.

Your middleware should add that information to the collection of features.

I can see how they are used in the codebase.

Since you have a property, which claims, it needs some time until the Result is there, it should be of type Task<ICompany>:

public Task<ICompany> Company => dbContext.GetCompany(host);

Having said that, I am showing now an option, which can initialize a class the async way.

Provide an async initialization method:

public class SessionService
{
    public string Company { get; private set; }

    //Inject DbContext via CTOR

    public Task InitializeAsync()
    {
        //Use DbContext, probably think about caching
        Company = "Async Company";
        return Task.CompletedTask;
    }
}

Then create a middleware which calls the InitializeAsync method*:

public class InitializationMiddleware : IMiddleware
{
    private readonly SessionService _sessionService;

    public InitializationMiddleware(SessionService sessionService)
    {
        _sessionService = sessionService;
    }

    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        await _sessionService.InitializeAsync();
        await next(context);
    }
}

Register everything into the ServiceCollection:

services.AddScoped<InitializationMiddleware>();
services.AddScoped<SessionService>();

Finaly use the middleware (probably after app.UseAuthorization();):

app.UseMiddleware<InitializationMiddleware>();

* As an alternative, you can make SessionService an IMiddleware itself, and initialize it in InvokeAsync. Then you would use app.UseMiddleware<SessionService>();

Related