1.在configservice中注入:services.AddScoped<ICurrentTenant, CurrentTenant>();
2.在中间件中赋值:_currentTenant = SetCurrentTenant(tenant);,此时的id,name,connectionString都是有值的
3.在controller中注入CurrentTenant,调试时看到只是new的一个空对象。
目前我的理解是:scope作用域,在一次http请求中,创建一个currentTenant实例,不管是在业务逻辑层还是在数据访问层都使用同一个currentTenant。那么currentTenant在中间件中赋值了,在controller中应该能够拿得到啊。
后来经过写日志,发现它们的hashcode不一样。
2021-01-11 09:53:47.9354|INFO|LS.Industry.BaseInfo.Api.Core.MultiTenantMiddleware|_currentTenant:59817589
2021-01-11 09:53:47.9354|INFO|LS.Industry.BaseInfo.Api.Core.MultiTenantMiddleware|_tenantRepository:48209832
2021-01-11 09:53:48.0385|INFO|LS.Industry.BaseInfo.Api.Controllers.HomeController|_currentTenant:64254500
2021-01-11 09:53:48.0385|INFO|LS.Industry.BaseInfo.Api.Controllers.HomeController|_tenantRepository:7167227
@dudu站长,各路大神求解惑。
提供源码看ICurrentTenant是采用的什么方式进行注入的。
ctor ? InvokeAsync?
startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
//全局
services.AddSingleton<ITenantResolver, QueryStringTenantResolver>();
services.AddScoped<ICurrentTenant, CurrentTenant>();
services.AddScoped<ITenantRepository, TenantRepository>();
//dapper会用到
services.Configure<ConnectionString>(Configuration.GetSection("ConnectionStrings"));
}
MultiTenantMiddleware:
private readonly RequestDelegate _nextDelegate;
private readonly ITenantResolver _tenantResolver;
private readonly ITenantRepository _tenantRepository;
private ICurrentTenant _currentTenant;//不能使用readonly,因为要改变它的值
public MultiTenantMiddleware(RequestDelegate nextDelegate
, ITenantResolver tenantResolver
, ITenantRepository tenantRepository
, ICurrentTenant currentTenant)
{
_nextDelegate = nextDelegate;
_tenantResolver = tenantResolver;
_tenantRepository = tenantRepository;
_currentTenant = currentTenant;
}
controller:
private readonly ITenantRepository _tenantRepository;
private readonly ICurrentTenant _currentTenant;
public HomeController(ITenantRepository tenantRepository
, ICurrentTenant currentTenant)
{
_tenantRepository = tenantRepository;
_currentTenant = currentTenant;
}
谢谢您,这是我的部分代码。
@屌丝大叔的笔记: 使用构造(ctor)注入的始终是单例的,并且Scope是不能注册在root provider
的,可以参见我写的demo。
@屌丝大叔的笔记:
我进行了一个测试,就是你这个原因,因为项目中肯定不验证Scope的,所以导致中间件中ctor注册scope服务是无效的,scope应该在Invoke
中使用。
Null
附上我的测试代码:
namespace cnblog_133057
{
public interface ICurrentTenant
{
string TenantId { get; }
string ConnectionString { get; }
void SetTenant(string id, string conn);
}
public class CurrentTenant : ICurrentTenant
{
public string TenantId { get; private set; }
public string ConnectionString { get; private set; }
public void SetTenant(string id, string conn)
{
TenantId = id;
ConnectionString = conn;
}
}
}
namespace cnblog_133057
{
public class TenantMiddleware
{
private readonly RequestDelegate _next;
private readonly ICurrentTenant _currentTenant;
public TenantMiddleware(RequestDelegate next, ICurrentTenant currentTenant)
{
_next = next;
_currentTenant = currentTenant;
}
public async Task InvokeAsync(HttpContext httpContext)
{
var __tenantId = httpContext.Request.Headers["__tenantId"];
var conn = "xxxxxx";
_currentTenant.SetTenant(__tenantId, conn);
await _next(httpContext);
}
}
}
app.UseMiddleware(typeof(TenantMiddleware));
namespace cnblog_133057.Controllers
{
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private readonly ICurrentTenant _tenant;
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger, ICurrentTenant tenant)
{
_logger = logger;
_tenant = tenant;
}
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)],
Tenant = $"{_tenant.TenantId};{_tenant.ConnectionString}"
})
.ToArray();
}
}
}
上面在模板项目中运行是会报错的,下面我为了测试强制不进行报错,添加如下代码
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseDefaultServiceProvider(configure =>
{
configure.ValidateOnBuild = false;
configure.ValidateScopes = false;
});
webBuilder.UseStartup<Startup>();
});
配置不验证
ValidateScopes
和在编译的时候不进行验证ValidateOnBuild
变更一下Invoke
public async Task InvokeAsync(HttpContext httpContext, ICurrentTenant _currentTenant)
{
var __tenantId = httpContext.Request.Headers["__tenantId"];
var conn = "xxxxxx";
_currentTenant.SetTenant(__tenantId, conn);
await _next(httpContext);
}
@Jonny-Xhl: 牛逼啊,大哥。我看了你给的链接,换成以下方式就行了:
var _currentTenant = httpContext.RequestServices.GetRequiredService<ICurrentTenant>();
_currentTenant.ChangeValue(tenant);
@屌丝大叔的笔记: 中间件构造中注入慎用!!!,很容易导致内存泄漏
@Jonny-Xhl: 知道了,谢谢。
建议提供 middleware 中的相关代码
– dudu 3年前方式一:
var _currentTenant = httpContext.RequestServices.GetRequiredService<ICurrentTenant>();
_currentTenant.ChangeValue(tenant);
方式二:
public async Task Invoke(HttpContext httpContext, ITenantRepository _tenantRepository, ICurrentTenant _currentTenant){
_tenantRepository.ChangeDatabase(string.Empty);
}
如Jonny-Xhl 所说,方式二比较优雅。
– 屌丝大叔的笔记 3年前@屌丝大叔的笔记: Invoke和反模式获取都是在Child Provider创建的,并非Root创建的。
– Jonny-Xhl 3年前