【问题标题】:Is there any way to make ITicketStore scoped?有什么方法可以使 ITicketStore 作用域?
【发布时间】:2021-05-28 16:37:13
【问题描述】:

我正在按照article 使用ITicketStore。每当需要执行任何数据库操作时,都会在此 DbContext 中创建。

在我的代码中,不是通过 using() 语法创建 DbContext,而是通过构造函数注入。在代码投入生产之前,一切都运行良好。 流量增加时开始出现以下异常。

  • System.InvalidOperationException 在前一个操作完成之前在此上下文中启动了第二个操作。
  • Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException

这可能是因为 ITicketStore 是一个单例并使用 DI 注入 DbContext,在多个线程之间共享相同的 DbContext 实例。

我更改了代码以通过 using() 创建一个 DbContext 实例,现在它工作正常。

但我正在尝试通过 DI 注入来找出使 DbContext 工作的任何方法。

【问题讨论】:

  • 您是否错过了使用 dbcontext 的调用的任何等待?我在没有等待的情况下将数据库上下文称为一堆,因为最后我有一个WhenAll。这导致了类似的问题。
  • 不,在使用 dbcontext 时使用 await

标签: c# .net asp.net-identity identityserver4 asp.net-core-3.1


【解决方案1】:

这是我的做法

//in Startup.cs ConfigureServices method  

//I tried using the PostConfigure methods, but it didn't to work for me
services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<CookieAuthenticationOptions>, PostConfigureCookieAuthenticationOptionsTicketStore>());
    //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
    }

希望这会有所帮助;)

【讨论】:

    猜你喜欢
    • 2013-03-27
    • 2015-08-22
    • 1970-01-01
    • 1970-01-01
    • 2010-10-27
    • 2015-07-02
    • 1970-01-01
    相关资源
    最近更新 更多