Оба AddDistributedSqlServerCache()
и AddDistributedRedisCache()
регистрируют синглтон для IDistributedCache
: SqlServerCache
и RedisCache
соответственно. Поскольку зависимые компоненты зависят только от IDistributedCache
, все они получат одинаковую реализацию распределенного кэша (в зависимости от того, что было зарегистрировано последним).
Это, как правило, дизайн, потому что реализация, например, промежуточное программное обеспечение сеанса не должно заботиться о фактической зарегистрированной реализации IDistributedCache
. Это просто зависит от того, есть ли некоторые и использует это. И в равной степени другие службы также будут использовать только одну распределенную зависимость кэша.
Обычно не было бы способа обойти это. В конечном итоге вы можете создать какой-то адаптер, который сам реализует IDistributedCache
, а затем в зависимости от переданных аргументов делегировать либо в кэш SQL Server, либо в кэш Redis.
В вашем случае есть более простой способ. Поскольку ядро ASP.NET построено так, чтобы быть очень расширяемым, и большинство компонентов можно просто поменять местами другими реализациями, мы можем использовать это здесь, чтобы промежуточное ПО сеанса просто использовало специализированный распределенный кеш, а все остальное возвращается к кешу по умолчанию.
Для этого мы просто реализуем ISessionStore
и регистрируем это, что в основном и делает AddSession()
. В реализации пользовательского хранилища сеансов вместо зависимости от 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>();
И это все. Поэтому, когда промежуточное программное обеспечение сеанса теперь разрешает ISessionStore
, оно получит SqlServerCacheSessionStore
, которое напрямую зависит от SqlServerCache
вместо общего IDistributedCache
, которое будет кешем Redis.