C# ASP.NET核心MVC异步加载会话

C# ASP.NET核心MVC异步加载会话,c#,asp.net,dependency-injection,asp.net-core,asp.net-core-mvc,C#,Asp.net,Dependency Injection,Asp.net Core,Asp.net Core Mvc,我一直在阅读官方文件,无意中发现了以下段落: 异步加载会话 ASP.NET Core中的默认会话提供程序加载会话记录 仅当 在TryGetValue之前显式调用ISession.LoadAsync方法, 设置或删除方法。如果未首先调用LoadAsync,则 底层会话记录是同步加载的,这可能会 可能会影响应用程序的扩展能力 若要让应用程序强制执行此模式,请将 DistributedSessionStore和DistributedSession的实现 未调用LoadAsync方法时引发异常的版本 在

我一直在阅读官方文件,无意中发现了以下段落:

异步加载会话

ASP.NET Core中的默认会话提供程序加载会话记录 仅当 在TryGetValue之前显式调用ISession.LoadAsync方法, 设置或删除方法。如果未首先调用LoadAsync,则 底层会话记录是同步加载的,这可能会 可能会影响应用程序的扩展能力

若要让应用程序强制执行此模式,请将 DistributedSessionStore和DistributedSession的实现 未调用LoadAsync方法时引发异常的版本 在TryGetValue之前,设置或删除在中注册包装的版本 服务容器

包装本身不是我的问题,但为了实现它,我需要:

  • 对原始实施的参考
  • 注册包装版本
  • 目前,我已经创建了以下包装类:

    public class WrappedDistributedSession : ISession
      {
        private DistributedSession _service;
        private bool loaded = false;
    
        public WrappedDistributedSession(DistributedSession service)
        {
          _service = service;
        }
    
        public bool IsAvailable => _service.IsAvailable;
    
        public string Id => _service.Id;
    
        public IEnumerable<string> Keys => _service.Keys;
    
        public void Clear() => _service.Clear();
    
        public Task CommitAsync() => _service.CommitAsync();
    
        public Task LoadAsync()
        {
          loaded = true;
          return _service.LoadAsync();
        }
    
        public void Remove(string key)
        {
          if(loaded)
          {
            _service.Remove(key);
          } else
          {
            throw new Exception();
          }
        }
    
        public void Set(string key, byte[] value)
        {
          if (loaded)
          {
            _service.Set(key, value);
          }
          else
          {
            throw new Exception();
          }
        }
    
        public bool TryGetValue(string key, out byte[] value)
        {
          if (loaded)
          {
            return _service.TryGetValue(key, out value);
          }
          else
          {
            throw new Exception();
          }
        }
      }
    

    显然,由于我写的是这个问题,我的解决方案不起作用。我哪里出错了?如何“在服务容器中注册包装好的版本”

    似乎您也需要实现
    ISessonStore
    (您引用的文档中实际提到了这一点),因为它是在
    AddSession
    扩展方法中注册的唯一版本

    public static IServiceCollection AddSession(this IServiceCollection services)
    {
        if (services == null)
        {
            throw new ArgumentNullException(nameof(services));
        }
    
        services.AddTransient<ISessionStore, DistributedSessionStore>();
        services.AddDataProtection();
        return services;
    }
    

    使用风险自负。这似乎在会话之后的Configure方法中起作用。 此解决方案是此单元测试的改编:

    或者作为更合格的包装扩展:

    public static class SesssionAsyncExtensions
    {
        /// <summary>
        /// Have sessions be asyncronous. This adaptation is needed to force the session provider to use async calls instead of syncronous ones for session. 
        /// Someone surprisingly for something that seems common, Microsoft didn't make this aspect super nice.
        /// </summary>
        /// <param name="app">App builder instance.</param>
        /// <returns>App builder instance for chaining.</returns>
        /// <remarks>
        /// From Microsoft Documentation (https://docs.microsoft.com/en-us/aspnet/core/fundamentals/app-state?view=aspnetcore-5.0):
        /// The default session provider in ASP.NET Core will only load the session record from the underlying IDistributedCache store asynchronously if the
        /// ISession.LoadAsync method is explicitly called before calling the TryGetValue, Set or Remove methods. 
        /// Failure to call LoadAsync first will result in the underlying session record being loaded synchronously,
        /// which could potentially impact the ability of an application to scale.
        /// 
        /// See also:
        /// https://github.com/dotnet/aspnetcore/blob/d2a0cbc093e1e7bb3e38b55cd6043e4e2a0a2e9a/src/Middleware/Session/src/DistributedSession.cs#L268
        /// https://github.com/dotnet/AspNetCore.Docs/issues/1840#issuecomment-454182594
        /// https://bartwullems.blogspot.com/2019/12/aspnet-core-load-session-state.html
        /// </remarks>
        public static IApplicationBuilder UseAsyncSession(this IApplicationBuilder app)
        {
            app.UseSession();
            app.Use(async (context, next) =>
            {
                await context.Session.LoadAsync();
                await next();
            });
            return app;
        }
    }
    
    公共静态类sessionAsyncExtensions
    {
    /// 
    ///使会话为异步的。需要此自适应来强制会话提供程序对会话使用异步调用而不是同步调用。
    ///令人惊讶的是,微软并没有让这方面变得非常好。
    /// 
    ///应用程序生成器实例。
    ///用于链接的应用程序生成器实例。
    /// 
    ///来自Microsoft文档(https://docs.microsoft.com/en-us/aspnet/core/fundamentals/app-state?view=aspnetcore-5.0):
    ///ASP.NET Core中的默认会话提供程序将仅在以下情况下从基础IDistributedCache存储异步加载会话记录:
    ///ISession.LoadAsync方法在调用TryGetValue、Set或Remove方法之前被显式调用。
    ///未能首先调用LoadAsync将导致同步加载基础会话记录,
    ///这可能会影响应用程序的扩展能力。
    /// 
    ///另见:
    /// https://github.com/dotnet/aspnetcore/blob/d2a0cbc093e1e7bb3e38b55cd6043e4e2a0a2e9a/src/Middleware/Session/src/DistributedSession.cs#L268
    /// https://github.com/dotnet/AspNetCore.Docs/issues/1840#issuecomment-454182594
    /// https://bartwullems.blogspot.com/2019/12/aspnet-core-load-session-state.html
    /// 
    公共静态IAApplicationBuilder UseAncySession(此IAApplicationBuilder应用程序)
    {
    app.UseSession();
    应用程序使用(异步(上下文,下一步)=>
    {
    wait context.Session.LoadAsync();
    等待下一个();
    });
    返回应用程序;
    }
    }
    
    您还有
    AddSession
    电话吗?更重要的是:您是否在AddSession之前或之后注册您的实现?这一点很重要,因为大多数
    AddXxx
    类使用
    TryAddcoped | Transient | Singleton
    而不是
    AddScoped | Transient | Singleton
    ,所以它必须在之前注册。与其实现
    ISession
    ,不如定义您自己的特定于应用程序的抽象。这个抽象可以很小(最好是一个成员),并且可以根据应用程序的需要进行定制。如果这样做,将大大简化实现,甚至不必“在TryGetValue之前未调用LoadAsync方法时引发异常”,因为这样可以防止在自己的会话实现中发生这种情况。如果您这样做,您将有效地遵循依赖项反转原则和接口分离原则。@Steven:那么,我应该编写自己的层来为我调用
    LoadAsync
    ,而不是使用
    ISession
    ?但是如果我通过
    HttpContext.session
    访问会话,在那里我可以直接引用
    ISession
    ?我不是这个项目的唯一开发人员,我更喜欢防弹解决方案。@alesc:我这里不是说图层。我只是说定义自己的接口,并将其放在已有的“适配器”实现上。此适配器可以放置在合成根目录中或其附近,此时您可以访问ASP.NET特定的所有内容,例如
    HttpContext
    。因此,您的适配器可以以适当的方式调用
    HttpContext.Session
    。@Steven给出答案很好,这通常会导致解决方案。您没有提供足够的信息来说明如何执行要求的操作,以注册包装版本。您似乎要求我们去学习如何以您认为合适的方式编写代码。我就像一个提问者。它不起作用,我也不知道为什么。诚然,微软的文档中应该有例子,但可惜的是,他们没有。我们到了。如果你知道怎么做,请发布一个解决方案。目前我还没有实现
    ISessonStore
    ,因为我不知道怎么做。所以基本上我必须重写这两个类,以便使用包装版本。在这种情况下,我是否仍然需要
    AddSession
    ,因为我已经手动注册了所有内容?
    ISessionStore
    只是一个工厂,具有一个
    Create
    方法。请参阅此处的默认分布式会话实现<代码>ISession不是通过DI解决的,而是通过此fac解决的
    services.AddTransient<ISessionStore, AsyncDistributedSessionStore>();
    
     app.UseSession();
     app.Use(async (context, next) =>
     {
        await context.Session.LoadAsync();
        await next();
     });
    
    public static class SesssionAsyncExtensions
    {
        /// <summary>
        /// Have sessions be asyncronous. This adaptation is needed to force the session provider to use async calls instead of syncronous ones for session. 
        /// Someone surprisingly for something that seems common, Microsoft didn't make this aspect super nice.
        /// </summary>
        /// <param name="app">App builder instance.</param>
        /// <returns>App builder instance for chaining.</returns>
        /// <remarks>
        /// From Microsoft Documentation (https://docs.microsoft.com/en-us/aspnet/core/fundamentals/app-state?view=aspnetcore-5.0):
        /// The default session provider in ASP.NET Core will only load the session record from the underlying IDistributedCache store asynchronously if the
        /// ISession.LoadAsync method is explicitly called before calling the TryGetValue, Set or Remove methods. 
        /// Failure to call LoadAsync first will result in the underlying session record being loaded synchronously,
        /// which could potentially impact the ability of an application to scale.
        /// 
        /// See also:
        /// https://github.com/dotnet/aspnetcore/blob/d2a0cbc093e1e7bb3e38b55cd6043e4e2a0a2e9a/src/Middleware/Session/src/DistributedSession.cs#L268
        /// https://github.com/dotnet/AspNetCore.Docs/issues/1840#issuecomment-454182594
        /// https://bartwullems.blogspot.com/2019/12/aspnet-core-load-session-state.html
        /// </remarks>
        public static IApplicationBuilder UseAsyncSession(this IApplicationBuilder app)
        {
            app.UseSession();
            app.Use(async (context, next) =>
            {
                await context.Session.LoadAsync();
                await next();
            });
            return app;
        }
    }