Хранение и извлечение значений из IDistributedCache (Redis) в.NET Core 2
У меня есть приложение ASP.NET Core2. Я использую встроенные и Autofac IoC контейнеры. Я настраиваю все регистрации компонентов в моем файле Startup.cs. При этом я также настраиваю свой DBContext, который наследуется от пользовательского DataContext, который, в свою очередь, наследуется от DbContext и реализует пользовательский IDataContextAsync. Этот DbContext ожидает строку подключения в качестве параметра конструктора.
Моя проблема заключается в том, что строка подключения хранится в Redis Cache, который является IDistributedCache. Кеш настраивается в файле startup.cs. Строка подключения также требуется в том же методе ConfigureServices в Startup.cs. Итак, у меня нет доступа к этому кешу на данный момент.
Все работало, когда я использовал сеанс HttpContext для хранения строки подключения. Теперь, когда приложение развертывается в веб-ферме, я не могу использовать его в сеансе proc. Мы используем Redis для государственного управления. Вот где у меня проблемы.
Вот мой метод ConfigureServices из файла startup.cs (для краткости удален ненужный код).
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.AddJsonOptions(op => op.SerializerSettings.ContractResolver = new DefaultContractResolver());
services.AddSession(opt =>
{
opt.IdleTimeout = TimeSpan.FromMinutes(20);
opt.Cookie.Name = "apexportal.RulesSession";
opt.Cookie.HttpOnly = true;
});
services.AddDistributedRedisCache(o =>
{
var host = Configuration.GetValue<string>($"{AppConstants.REDIS}:{AppConstants.REDISHOST}");
var port = Configuration.GetValue<string>($"{AppConstants.REDIS}:{AppConstants.REDISPORT}");
o.Configuration = $"{host}";
o.InstanceName = Configuration.GetValue<string>($"{AppConstants.REDIS}:{AppConstants.REDISNAME}");
});
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
//services.AddTransient<IConnectionStringProvider, ConnectionStringProvider>();
services.AddTransient<IDataContextAsync>(s => new PortalEFContext(GetPortalConnectionString()));
services.AddAuthentication(IISDefaults.AuthenticationScheme);
ContainerBuilder builder = new ContainerBuilder();
builder.Populate( services );
var container = builder.Build();
return container.Resolve<IServiceProvider>();
}
и вот мой метод GetPortalConnectionString(), который также находится в файле startup.cs. Я хочу заменить строку accessor.HttpContext.Session.Get() на вставленный RedisCache.Get().
private string GetPortalConnectionString()
{
IHttpContextAccessor accessor = new HttpContextAccessor();
//this is where I need to access the RedisCache and access the stored properties
// instead of using HttpContext.Session. But I don't know how to inject the IDistributedCache
// to this spot.
var connString = accessor.HttpContext.Session.Get<string>(AppConstants.SPCONNSTRING);
return connString ?? Configuration.GetConnectionString("PortalEFContext");
}
Позже, когда пользователь выбрал базу данных для использования в приложении, я так же храню строку подключения к этой базе данных в Redis Cache.
Вот мой класс BaseController, который делает это.
public abstract class BaseController : Controller
{
//private readonly IRulesEngineService reService;
protected readonly IHttpContextAccessor httpCtxAccessor;
protected readonly IConfiguration config;
private readonly IAuthService authService;
protected readonly IDistributedCache redisCache;
public BaseController(IHttpContextAccessor _httpContext, IConfiguration _config, IAuthService _authService, IDistributedCache _redisCache)
{
//reService = _reService;
httpCtxAccessor = _httpContext;
config = _config;
authService = _authService;
redisCache = _redisCache;
//SetupCurrentWindowsUserAsync();
}
protected async Task<string> SetCurrentDBConnString( int dbId )
{
var currDbId = await GetCurrentDBId();
if ( currDbId == 0 || currDbId != dbId )
{
var envConnStr = config.GetConnectionString( AppConstants.ENVCONNSTRING );
var connStr = await AppHelper.SetCurrentDBConnectionString( dbId, envConnStr );
//httpCtxAccessor.HttpContext.Session.Set<string>( AppConstants.SPCONNSTRING, connStr );
//httpCtxAccessor.HttpContext.Session.Set<int>( AppConstants.CURRDBID, dbId );
await redisCache.SetAsync<string>( AppConstants.SPCONNSTRING, connStr );
await redisCache.SetAsync<int>( AppConstants.CURRDBID, dbId );
await SetupCurrentWindowsUserAsync();
return connStr;
}
return null;
}
}
Может кто-нибудь сказать мне, как я могу получить доступ к кэш Redis в моем файле startup.cs? Благодарю.
1 ответ
Это на самом деле очень просто. Вы были уже почти там.
Внимательно посмотрите на эту строку в вашем стартапе:
services.AddTransient<IDataContextAsync>(s => new PortalEFContext(GetPortalConnectionString()));
Увидеть s
параметр в лямбде? Это DI-контейнер.NET Core, который называется IServiceProvider
, Это то, что вы искали. Просто передайте его в вашу функцию и используйте там, чтобы решить все, что вы хотите.
Итак, код будет следующим:
public IServiceProvider ConfigureServices(IServiceCollection services)
...
services.AddTransient<IDataContextAsync>(s => new PortalEFContext(GetPortalConnectionString(s))); // <-- pass the container to the function
...
}
private string GetPortalConnectionString(IServiceProvider container)
{
// Here you go:
var cache = container.GetService<IDistributedCache>();
// and now do whatever you want with it.
var connString = cache.Get<string>(AppConstants.SPCONNSTRING);
// BTW, configuration can be resolved from container as well in order to avoid hard dependency on global Configuration object:
var config = container.GetService<IConfiguration>();
return connString ?? config.GetConnectionString("PortalEFContext");
}