在.net核心应用程序中,我有一个ISessionService(它只包含各种可重用的属性,比如基于当前URL的当前"公司"。
它在启动时定义如下:
services.AddScoped<ISessionService, SessionService>();
SessionService的属性是.Company。但在.Company中,会对数据库进行调用,因为它是一个属性,所以无法等待。所以目前看起来像
public ICompany Company => dbContext.GetCompany(host).Result
.Result导致应用程序在重负载下挂起。我可以把它放在SessionService的构造函数中,但这只是把.Result从属性移到构造函数中,并导致同样的问题。
有没有一种方法,也许通过初始化SessionService,以异步方式初始化,而不必执行任何同步调用,如.Result?
不要引入任何异步代码的同步阻塞。您将损害应用程序的性能和可扩展性。
您想要的东西应该通过中间件和功能的组合来完成。
您的中间件应该将该信息添加到特性集合中。
我可以看到它们是如何在代码库中使用的。
由于您有一个声明的属性,它需要一些时间才能得到结果,因此它的类型应该是Task<ICompany>
:
public Task<ICompany> Company => dbContext.GetCompany(host);
话虽如此,我现在展示了一个选项,它可以以异步方式初始化类。
提供一种异步初始化方法:
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;
}
}
然后创建一个中间件,该中间件调用InitializeAsync
方法*:
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);
}
}
将所有内容注册到ServiceCollection:
services.AddScoped<InitializationMiddleware>();
services.AddScoped<SessionService>();
最终使用中间件(可能在app.UseAuthorization();
之后(:
app.UseMiddleware<InitializationMiddleware>();
*作为替代方案,您可以将SessionService
本身设置为IMiddleware
,并在InvokeAsync
中对其进行初始化。然后您将使用app.UseMiddleware<SessionService>();