在.NET Core 2

时间:2018-04-10 22:12:44

标签: asp.net-core dependency-injection redis autofac distributed-caching

我有一个ASP.NET Core2应用程序。我正在使用内置和Autofac IoC容器。我正在我的Startup.cs文件中设置所有组件注册。在执行此操作时,我还设置了我的DBContext,它继承自自定义DataContext,后者继承自DbContext并实现自定义IDataContextAsync。此DbContext期望连接字符串作为构造函数参数。

我的问题是连接字符串存储在Redis缓存中,这是一个IDistributedCache。缓存在startup.cs文件中设置。 Startup.cs中的相同ConfigureServices方法中也需要连接字符串。所以,此时我似乎无法访问此缓存。

当我使用HttpContext会话存储连接字符串时,一切正常。现在应用程序正在部署到Web场,我无法在proc会话中使用。我们正在使用Redis进行状态管理。这是我遇到问题的地方。

这是我在startup.cs文件中的ConfigureServices方法(为简洁起见,删除了不必要的代码)。

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文件中。我想用注入的RedisCache.Get()替换行accessor.HttpContext.Session.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缓存中的那个数据库中。

这是我的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;
  }
}

有人可以告诉我如何在startup.cs文件中访问Redis缓存吗?感谢。

1 个答案:

答案 0 :(得分:1)

实际上非常简单。你几乎已经在那里了。

仔细看看你的创业公司中的这一行:

services.AddTransient<IDataContextAsync>(s => new PortalEFContext(GetPortalConnectionString()));

请参阅lambda中的s参数?这是.NET Core的DI容器,名为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");
}