我正在按照这篇文章使用ITicketStore。每当需要执行任何数据库操作时,都会创建此 DbContext。
在我的代码中,不是通过 using() 语法创建 DbContext,而是通过构造函数注入。在代码投入生产之前,一切都工作正常。 当流量增加时开始出现以下异常。
这可能是因为 ITicketStore 是一个单例,并且使用在多个线程之间共享相同 DbContext 实例的 DI 注入 DbContext。
我更改了代码以通过 using() 创建 DbContext 实例,现在工作正常。
但是我正在尝试找到任何方法通过 DI 注入来使 DbContext 工作。
ITicketStore 必须是单例,但您可以注册主单例,并使用
CreateScope()
方法注入您可能需要的任何作用域提供程序。您可以在下面找到使用 SingletonTicketStore
作为包装器的示例实现,并使用 PostConfigureCookieAuthenticationOptionsTicketStore
来使用适当商店中的 CookieAuthenticationOptions
。
请注意,并非所有情况都需要 PostConfigure。
//This sets the store to the options.
public class PostConfigureCookieAuthenticationOptionsTicketStore : IPostConfigureOptions<CookieAuthenticationOptions>
{
private readonly SingletonTicketStore _store;
public PostConfigureCookieAuthenticationOptionsTicketStore(SingletonTicketStore store) => _store = store;
public void PostConfigure(string name, CookieAuthenticationOptions options) => options.SessionStore = _store;
}
//This implemenation is singleton compatible
public class SingletonTicketStore : ITicketStore
{
private readonly IServiceProvider _services;
public SingletonTicketStore(IServiceProvider services)
{
_services = services;
}
public async Task RemoveAsync(string key) => await WithScopeAsync(s => s.RemoveAsync(key));
public async Task RenewAsync(string key, AuthenticationTicket ticket) => await WithScopeAsync(s => s.RenewAsync(key, ticket));
public async Task<AuthenticationTicket> RetrieveAsync(string key) => await WithScopeAsync(s => s.RetrieveAsync(key));
public async Task<string> StoreAsync(AuthenticationTicket ticket) => await WithScopeAsync(s => s.StoreAsync(ticket));
private async Task WithScopeAsync(Func<ITicketStore, Task> action) => await WithScopeAsync(async t => { await action(t); return true; });
private async Task<T> WithScopeAsync<T>(Func<ITicketStore, Task<T>> action)
{
//This opens a new scope, allowing you to use scoped dependencies
using (var scope = _services.CreateScope())
{
//don't forget to await the result here, of stuff (dbcontext) could be disposed otherwise
return await action(scope.ServiceProvider.GetRequiredService<ITicketStore>());
}
}
}
public class ScopedTicketStore : ITicketStore
{
//your implementation
}
//in Startup.cs ConfigureServices method
//register Singleton ticket store as ITicketStore and as standalone singleton
services.AddSingleton<SingletonTicketStore>();
services.AddSingleton<ITicketStore, SingletonTicketStore>(serviceProvider =>
serviceProvider.GetRequiredService<SingletonTicketStore>());
//register your store as scoped (without specifying interface)
services.AddScoped<ScopedTicketStore>();
// optional
services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<CookieAuthenticationOptions>, PostConfigureCookieAuthenticationOptionsTicketStore>());
希望这有帮助;)