ASP.NET 如何根据请求内容动态修改数据库连接字符串
解决方案
第一步:调整NHibernate会话工厂的实现
你当前的会话工厂实现绑定了固定连接串,需要先改造为支持动态传入连接串创建会话:
// 调整INHibernateSessionFactory接口,新增支持传入连接串的方法 public interface INHibernateSessionFactory { ISession OpenSession(string connectionString); } // 对应实现类调整 public class NHibernateSessionFactory : INHibernateSessionFactory { // 移除构造函数中的固定连接串参数,原有映射加载、配置初始化逻辑保持不变 public NHibernateSessionFactory() { // 原有NHibernate配置、映射文件加载等逻辑不需要修改 } public ISession OpenSession(string connectionString) { // 使用传入的动态连接串创建会话 return Configuration.BuildSessionFactory().OpenSession(connectionString); } }
第二步:修改Startup服务注册配置
删除原有静态绑定固定连接串的注册逻辑,替换为无固定配置的会话工厂注册:
// 注册无固定连接串的会话工厂为单例 services.AddSingleton<INHibernateSessionFactory, NHibernateSessionFactory>(); // 移除原有固定连接串的ISession、IDataContext注册,改为后续动态创建
第三步:请求中动态构造连接串获取数据上下文
可根据业务场景选择以下两种实现方式:
方式1:直接在控制器中处理(逻辑清晰,适合少量接口需要动态切库的场景)
// 请求参数接收模型 public class DbAccessDto { public string DbIp { get; set; } public string DbName { get; set; } // 其他业务参数 } [ApiController] [Route("api/[controller]")] public class DataController : ControllerBase { private readonly INHibernateSessionFactory _sessionFactory; private readonly IConfiguration _configuration; public DataController(INHibernateSessionFactory sessionFactory, IConfiguration configuration) { _sessionFactory = sessionFactory; _configuration = configuration; } [HttpPost] public IActionResult Post([FromBody] DbAccessDto dto) { // 1. 校验参数合法性,必须配置白名单校验,避免非法访问未授权数据库 if (!ValidateDbAccessParams(dto.DbIp, dto.DbName)) { return BadRequest("非法的数据库访问参数"); } // 2. 使用连接串构造器安全拼接,避免注入风险和特殊字符问题 var connectionStringBuilder = new SqlConnectionStringBuilder { DataSource = dto.DbIp, InitialCatalog = dto.DbName, UserID = _configuration["Db:CommonUsername"], // 用户名、密码等固定配置从配置文件读取 Password = _configuration["Db:CommonPassword"], // 其他固定连接参数按需配置 }; var dynamicConnString = connectionStringBuilder.ToString(); // 3. 动态创建会话和数据上下文 using var session = _sessionFactory.OpenSession(dynamicConnString); using var dataContext = new DataContext(session); // 4. 执行业务逻辑 // var result = dataContext.YourBusinessQuery(); return Ok(result); } private bool ValidateDbAccessParams(string ip, string dbName) { // 实现自定义校验逻辑,比如只允许访问白名单内的IP和数据库 return true; } }
方式2:全局统一处理(适合大量接口都需要动态切库的场景)
如果大部分接口都需要动态切换数据库,可以通过中间件+Scoped工厂的方式统一处理,不需要每个控制器重复写逻辑:
- 补充服务注册逻辑:
// 注册HttpContext访问器,用于读取请求参数 services.AddHttpContextAccessor(); // 注册IDataContext为Scoped,每个请求动态构造 services.AddScoped<IDataContext>(provider => { var httpContext = provider.GetRequiredService<IHttpContextAccessor>().HttpContext; var sessionFactory = provider.GetRequiredService<INHibernateSessionFactory>(); var configuration = provider.GetRequiredService<IConfiguration>(); // 从请求头/表单中读取IP和数据库名参数 var dbIp = httpContext.Request.Headers["X-Db-Ip"].FirstOrDefault() ?? httpContext.Request.Form["DbIp"].FirstOrDefault(); var dbName = httpContext.Request.Headers["X-Db-Name"].FirstOrDefault() ?? httpContext.Request.Form["DbName"].FirstOrDefault(); if (string.IsNullOrEmpty(dbIp) || string.IsNullOrEmpty(dbName)) { throw new InvalidOperationException("缺少数据库访问参数"); } // 拼接连接串 var connectionStringBuilder = new SqlConnectionStringBuilder { DataSource = dbIp, InitialCatalog = dbName, UserID = configuration["Db:CommonUsername"], Password = configuration["Db:CommonPassword"] }; var session = sessionFactory.OpenSession(connectionStringBuilder.ToString()); return new DataContext(session); });
- 之后在控制器中直接注入
IDataContext即可使用,不需要每次手动创建。
注意事项
- 必须对传入的IP和数据库名称做严格的白名单校验,避免出现未授权的数据库访问风险。
- 不同连接串会对应独立的数据库连接池,并发请求访问大量不同数据库时需要关注连接池占用情况。
- 如果NHibernate的配置初始化开销较大,可以对不同连接串对应的SessionFactory做缓存,避免重复初始化。
内容的提问来源于stack exchange,提问作者Manuel_dev
相关产品推荐
相关产品推荐

