ASP.NET 核心 2.x 中间件的自定义依赖注入解析器?
Custom Dependency Injection Resolver for ASP.NET Core 2.x Middleware?
给定一个 ASP.NET 核心 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
,它们都将获得相同的分布式缓存实现(取决于最后注册的内容)。
这通常是设计使然,因为实现,例如会话中间件,不应该关心 IDistributedCache
的实际注册实现是什么。它只取决于有 some 并使用它。同样,其他服务也将只使用一个分布式缓存依赖项。
通常,真的没有办法解决这个问题。您最终可以做的是创建一些实现 IDistributedCache
本身的适配器,然后根据传递的参数委托给 SQL 服务器缓存或 Redis 缓存。
对于您的情况,还有一种更简单的方法。由于 ASP.NET Core 的构建具有很强的可扩展性,并且大多数组件都可以被其他实现简单地交换,我们可以在这里利用它来使会话中间件只使用专门的分布式缓存,而其他一切都回退到默认缓存.
为此,我们只需实施 ISessionStore
并注册它,这基本上也是 AddSession()
所做的。在自定义会话存储实现中,我们将直接依赖 SqlServerCache
而不是依赖于 IDistributedCache
。这样一来,我们就不会退回到默认的 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>();
这应该就是全部了。因此,当会话中间件现在解析 ISessionStore
时,它将获得直接依赖于 SqlServerCache
的 SqlServerCacheSessionStore
而不是一般的 IDistributedCache
,后者将成为 Redis 缓存。
给定一个 ASP.NET 核心 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
,它们都将获得相同的分布式缓存实现(取决于最后注册的内容)。
这通常是设计使然,因为实现,例如会话中间件,不应该关心 IDistributedCache
的实际注册实现是什么。它只取决于有 some 并使用它。同样,其他服务也将只使用一个分布式缓存依赖项。
通常,真的没有办法解决这个问题。您最终可以做的是创建一些实现 IDistributedCache
本身的适配器,然后根据传递的参数委托给 SQL 服务器缓存或 Redis 缓存。
对于您的情况,还有一种更简单的方法。由于 ASP.NET Core 的构建具有很强的可扩展性,并且大多数组件都可以被其他实现简单地交换,我们可以在这里利用它来使会话中间件只使用专门的分布式缓存,而其他一切都回退到默认缓存.
为此,我们只需实施 ISessionStore
并注册它,这基本上也是 AddSession()
所做的。在自定义会话存储实现中,我们将直接依赖 SqlServerCache
而不是依赖于 IDistributedCache
。这样一来,我们就不会退回到默认的 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>();
这应该就是全部了。因此,当会话中间件现在解析 ISessionStore
时,它将获得直接依赖于 SqlServerCache
的 SqlServerCacheSessionStore
而不是一般的 IDistributedCache
,后者将成为 Redis 缓存。