用于ASP.NET Core 2.x中间件的自定义依赖项注入解析器?

时间:2018-08-28 22:45:04

标签: c# asp.net-core asp.net-core-middleware

鉴于一个ASP.NET Core 2.x应用程序,假设我使用了两种分布式缓存机制:

services.AddDistributedSqlServerCache()
services.AddDistributedRedisCache()

据我了解,由于Redis是最后一次注册,因此,每当请求IDistributedCache的实例时,它都会解析为RedisCache的实现。

在我的项目中,我还使用了Distributed-Cache标签助手,我想将其解析为RedisCache(没问题,可以使用上述设置)。

但是,我也正在使用会话中间件,该中间件还要求实现IDistributedCache

我需要Session中间件来解析到SQL分布式缓存和Distributed-Cache标签助手,以及对IDistributedCache缓存来解析到RedisCache的任何其他请求

如果我正确理解this的文章,则可以指定服务定位器针对对services.AddSingleton的通用调用解析为哪种实现,但这似乎并不能翻译{{1 }}。

有什么办法解决这个问题吗?

1 个答案:

答案 0 :(得分:3)

AddDistributedSqlServerCache()AddDistributedRedisCache()分别为IDistributedCache注册一个单例:SqlServerCacheRedisCache。由于相关组件仅依赖于IDistributedCache,因此它们都将获得相同的分布式缓存实现(取决于上次注册的内容)。

这通常是设计使然,因为实现例如会话中间件应该不关心IDistributedCache的实际注册实现是什么。它仅取决于存在 some 并使用它。同样,其他服务也将只使用一个分布式缓存依赖项。

通常,解决这个问题真的没有办法。您最终可以做的是创建一些适配器,该适配器本身实现IDistributedCache,然后根据传递的参数委托给SQL Server缓存或Redis缓存。

在您的情况下,有一种更简单的方法。由于ASP.NET Core的构建具有很高的可扩展性,并且大多数组件可以由其他实现简单地交换,因此我们可以在此处利用它使会话中间件仅使用专用的分布式缓存,而其他所有内容都归还默认缓存。 / p>

为此,我们只需实现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的{​​{1}}而不是一般的SqlServerCache,后者将是Redis缓存。