Reputation: 577
I have an ASP.NET Core2 application. I am using both builtin and Autofac IoC containers. I am setting up all the component registrations in my Startup.cs file. While doing this, I am also setting up my DBContext which inherits from a custom DataContext which in turn inherits from DbContext and implements a custom IDataContextAsync. This DbContext expects a connection string as a constructor parameter.
My problem is that the connection string is stored in the Redis Cache which is an IDistributedCache. The cache is setup in the startup.cs file. The Connection String also is required in the same ConfigureServices method in Startup.cs. So, I don't seem to have access to this cache at this point.
Everything was working when I was using the HttpContext Session to store the connection string. Now that the application is being deployed to a Web farm, I can't use in proc session. We are using Redis for state management. This is where I am having a problem with.
Here is my ConfigureServices method from startup.cs file (unnecessary code removed for brevity).
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>();
}
and here is my GetPortalConnectionString() method which is also in the startup.cs file. I want to replace the line accessor.HttpContext.Session.Get() with an injected 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");
}
Later when the user has selected a database to use in the application, I am storing the connectionstring to that database in Redis Cache like so.
Here is my BaseController class which does that.
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;
}
}
Can someone please tell me how I can access the Redis cache in my startup.cs file? Thanks.
Upvotes: 0
Views: 3844
Reputation: 4794
It's actually very simple. You were almost there already.
Take a closer look at this line in your startup:
services.AddTransient<IDataContextAsync>(s => new PortalEFContext(GetPortalConnectionString()));
See the s
parameter in the lambda? This is the DI container of .NET Core called IServiceProvider
. This is what you were looking for. Just pass it down into your function and use it there to resolve anything you want.
So, the code will be the following:
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");
}
Upvotes: 1