Both AddDistributedSqlServerCache() and AddDistributedRedisCache() register a singleton for IDistributedCache: SqlServerCache and RedisCache respectively. Since dependent components just depend on IDistributedCache, they will all get the same distributed cache implementation (depending on what was registered last).
This is generally by design because implementation, e.g. the session middleware, should not care about what the actual registered implementation of IDistributedCache is. It just depends on there being some and uses that. And equally, other services will also just use the one distributed cache dependency.
Usually, there wouldn’t really be a way around this. What you could eventually do is create some adapter that implements IDistributedCache itself and then delegates to either the SQL Server cache or the Redis cache depending on the passed arguments.
In your case, there is a simpler way though. Since ASP.NET Core is built to be very extensible, and most components can be simply swapped by other implementations, we can utilize this here to make the session middleware just use a specialized distributed cache while everything else falls back to the default cache.
For this, we just implement ISessionStore and register that, which is basically what AddSession() also does. In the custom session store implementation, instead of depending on IDistributedCache we will just depend on SqlServerCache directly. So that way, we do not fall back to the default IDistributedCache (whatever that would be), but force the system to use a 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);
}
}
This is literally the same implementation as DistributedSessionStore, which is the default ISessionStore implementation, except that we depend on SqlServerCache instead of IDistributedCache.
Now, we just need to wire everything up in the Configure method:
// 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>();
And that should be all. So when the session middleware now resolves the ISessionStore it will get the SqlServerCacheSessionStore which directly depends on the SqlServerCache instead of the general IDistributedCache which will be the Redis cache.