给定一个 ASP.NET Core 2.x 应用程序,假设我正在使用两种分布式缓存机制:
services.AddDistributedSqlServerCache()
services.AddDistributedRedisCache()
据我了解,由于 Redis 是最后注册的,因此每当请求
IDistributedCache
的实例时,它都会解析为 RedisCache
实现。在我的项目中,我还使用了
Distributed-Cache
标签助手,我想将其解析为 RedisCache
(没问题,适用于上述设置)。然而,我也在使用 Session 中间件,它也需要一个
IDistributedCache
实现。我需要
Session
中间件来解析 SQL 分布式缓存和 Distributed-Cache
标签助手,以及对 IDistributedCache
缓存的任何其他请求来解析 RedisCache
。如果我正确理解 this 文章,您可以指定服务定位器解析为对
services.AddSingleton
的通用调用的哪个实现,但这似乎并没有转换像 AddSession()
这样的中间件注册辅助函数。任何想法如何解决这个问题?
最佳答案
AddDistributedSqlServerCache()
和 AddDistributedRedisCache()
都为 IDistributedCache
注册了一个单例: SqlServerCache
和 RedisCache
。由于依赖组件仅依赖于 IDistributedCache
,因此它们都将获得相同的分布式缓存实现(取决于最后注册的内容)。
这通常是设计使然,因为实现,例如 session 中间件,不应该关心 IDistributedCache
的实际注册实现是什么。它只取决于有一些并使用它。同样,其他服务也将只使用一种分布式缓存依赖项。
通常,真的没有办法解决这个问题。您最终可以做的是创建一些实现 IDistributedCache
本身的适配器,然后根据传递的参数委托(delegate)给 SQL Server 缓存或 Redis 缓存。
在您的情况下,有一种更简单的方法。由于 ASP.NET Core 构建为非常可扩展,并且大多数组件可以简单地由其他实现交换,我们可以在这里利用它使 session 中间件只使用专门的分布式缓存,而其他一切都回退到默认缓存。
为此,我们只需实现 ISessionStore
并注册它,这基本上也是 AddSession()
所做的。在自定义 session 存储实现中,我们不依赖于 IDistributedCache
,而是直接依赖于 SqlServerCache
。这样,我们就不会退回到默认的 IDistributedCache
(无论是什么),而是强制系统使用 SqlServerCache
。
public class SqlServerCacheSessionStore : ISessionStore
{
private readonly IDistributedCache _cache;
private readonly ILoggerFactory _loggerFactory;
public SqlServerCacheSessionStore(SqlServerCache cache, ILoggerFactory loggerFactory)
{
_cache = cache ?? throw new ArgumentNullException(nameof(cache));
_loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
}
public ISession Create(string sessionKey, TimeSpan idleTimeout, TimeSpan ioTimeout, Func<bool> tryEstablishSession, bool isNewSessionKey)
{
if (string.IsNullOrEmpty(sessionKey))
throw new ArgumentNullException(nameof(sessionKey));
if (tryEstablishSession == null)
throw new ArgumentNullException(nameof(tryEstablishSession));
return new DistributedSession(_cache, sessionKey, idleTimeout, ioTimeout, tryEstablishSession, _loggerFactory, isNewSessionKey);
}
}
这实际上与
DistributedSessionStore
的实现相同,它是默认的 ISessionStore
实现,除了我们依赖 SqlServerCache
而不是 IDistributedCache
。现在,我们只需要在
Configure
方法中连接所有东西:// we keep the Redis cache as the default
services.AddDistributedRedisCache();
// no call to `AddSqlServerCache` as we don’t want to overwrite the `IDistributedCache`
// registration; instead, register (and configure) the SqlServerCache directly
services.AddSingleton<SqlServerCache>();
services.Configure<SqlServerCacheOptions>(options =>
{
// here goes the configuration that would normally be done in the
// configure action passed to `AddSqlServerCache`
options.ConnectionString = Configuration.GetConnectionString("DistributedCache");
});
// add session, but overwrite the `ISessionStore` afterwards
services.AddSession();
services.AddTransient<ISessionStore, SqlServerCacheSessionStore>();
这应该是全部。因此,当 session 中间件现在解析
ISessionStore
时,它将获得直接依赖于 SqlServerCacheSessionStore
的 SqlServerCache
而不是一般的 IDistributedCache
,后者将作为 Redis 缓存。关于c# - ASP.NET Core 2.x 中间件的自定义依赖注入(inject)解析器?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/52067152/