租户功能
Posted cloudsu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了租户功能相关的知识,希望对你有一定的参考价值。
1、AbpMultiTenancyModule模块,DefaultTenantStoreOptions存储配置租户信息TenantConfiguration数组,每个租户包括Guid,Name,ConnectionStrings
[DependsOn( typeof(AbpDataModule), typeof(AbpSecurityModule) )] public class AbpMultiTenancyModule : AbpModule public override void ConfigureServices(ServiceConfigurationContext context) var configuration = context.Services.GetConfiguration(); Configure<DefaultTenantStoreOptions>(configuration);
比如,如何从配置或ITenantStore得到租户信息
services.Configure<DefaultTenantStoreOptions>(options => options.Tenants = new[] new TenantConfiguration(_tenant1Id, "tenant1") ConnectionStrings = ConnectionStrings.DefaultConnectionStringName, "tenant1-default-value", "db1", "tenant1-db1-value" , new TenantConfiguration(_tenant2Id, "tenant2") ; );
services.Configure<DbConnectionOptions>(options => options.ConnectionStrings.Default = "default-value"; options.ConnectionStrings["db1"] = "db1-default-value"; );
另外关注MultiTenancyOptions
public class MultiTenancyOptions /// <summary> /// A central point to enable/disable multi-tenancy. /// Default: false. /// </summary> public bool IsEnabled get; set; /// <summary> /// Database style for tenants. /// Default: <see cref="MultiTenancyDatabaseStyle.Hybrid"/>. /// </summary> public MultiTenancyDatabaseStyle DatabaseStyle get; set; = MultiTenancyDatabaseStyle.Hybrid;
2、ICurrentTenant 当前租户,依赖ICurrentTenantAccessor来确定,关注Change方法IDisposable Change(Guid? id, string name = null);
public IDisposable Change(Guid? id, string name = null) return SetCurrent(id, name); private IDisposable SetCurrent(Guid? tenantId, string name = null) var parentScope = _currentTenantAccessor.Current; _currentTenantAccessor.Current = new BasicTenantInfo(tenantId, name); return new DisposeAction(() => _currentTenantAccessor.Current = parentScope; );
3、ICurrentTenantAccessor,依赖BasicTenantInfo信息来确定(null值使用host,不是null值使用租户信息),TenantId是指示是否赋值,AsyncLocal<BasicTenantInfo>
4、ITenantResolver,这里有一个传递ITenantResolveContext ,传递入IServiceProvider,通过ITenantResolveContributor的实现来确定当前租户TenantResolveResult,ITenantResolveContributor,从GetTenantIdOrNameFromHttpContextOrNull实现方法在Volo.Abp.AspNetCore.MultiTenancy模块,获取租户的Id和名称有五种方法,Cookie,Domain,Header,QueryString,Route,实现在模块Volo.Abp.AspNetCore.MultiTenancy里面
public interface ITenantResolveContributor
string Name get;
void Resolve(ITenantResolveContext context);
public abstract class TenantResolveContributorBase : ITenantResolveContributor
public abstract string Name get;
//TODO: We can make this async
public abstract void Resolve(ITenantResolveContext context);
public abstract class HttpTenantResolveContributorBase : TenantResolveContributorBase
。。。。
protected abstract string GetTenantIdOrNameFromHttpContextOrNull([NotNull] ITenantResolveContext context, [NotNull] HttpContext httpContext);
。。。。
[DependsOn( typeof(AbpMultiTenancyModule), typeof(AbpAspNetCoreModule) )] public class AbpAspNetCoreMultiTenancyModule : AbpModule public override void ConfigureServices(ServiceConfigurationContext context) Configure<TenantResolveOptions>(options => options.TenantResolvers.Add(new QueryStringTenantResolveContributor()); options.TenantResolvers.Add(new RouteTenantResolveContributor()); options.TenantResolvers.Add(new HeaderTenantResolveContributor()); options.TenantResolvers.Add(new CookieTenantResolveContributor()); );
public const string DefaultTenantKey = "__tenant";
其实现TenantResolver,通过遍历TenantResolveOptions下面的List<ITenantResolveContributor> TenantResolvers,获取得TenantIdOrName
6、ITenantStore,查找租户
[Dependency(TryRegister = true)] public class DefaultTenantStore : ITenantStore, ITransientDependency private readonly DefaultTenantStoreOptions _options; public DefaultTenantStore(IOptionsSnapshot<DefaultTenantStoreOptions> options) _options = options.Value; public Task<TenantConfiguration> FindAsync(string name) return Task.FromResult(_options.Tenants?.FirstOrDefault(t => t.Name == name)); public Task<TenantConfiguration> FindAsync(Guid id) return Task.FromResult(_options.Tenants?.FirstOrDefault(t => t.Id == id));
8、IConnectionStringResolver,解决连接字符串,MultiTenantConnectionStringResolver是替换实现服务, _connectionResolver.ShouldBeOfType<MultiTenantConnectionStringResolver>();
有实现如何获取字符串,包括从 ITenantStore查到TenantConfiguration或DbConnectionOptions或DefaultTenantStoreOptions配置文件
No tenant in current context:默认DbConnectionOptions
Overrided connection strings for tenant1:对应租户DefaultTenantStoreOptions
Undefined connection strings for tenant2:没有对应租户字符串使用默认字符串
public override string Resolve(string connectionStringName = null) //No current tenant, fallback to default logic if (_currentTenant.Id == null) return base.Resolve(connectionStringName); using (var serviceScope = _serviceProvider.CreateScope()) var tenantStore = serviceScope .ServiceProvider .GetRequiredService<ITenantStore>(); //租户存储获取,tenant-management进行管理 var tenant = AsyncHelper.RunSync(() => tenantStore.FindAsync(_currentTenant.Id.Value)); //TODO: Can we avoid from RunSync? // 当前租户没有连接字符串,则返回默认连接字符串 if (tenant?.ConnectionStrings == null) return base.Resolve(connectionStringName); //Requesting default connection string if (connectionStringName == null) return tenant.ConnectionStrings.Default ?? Options.ConnectionStrings.Default; //Requesting specific connection string var connString = tenant.ConnectionStrings.GetOrDefault(connectionStringName); if (connString != null) return connString; /* Requested a specific connection string, but it‘s not specified for the tenant. * - If it‘s specified in options, use it. * - If not, use tenant‘s default conn string. */ var connStringInOptions = Options.ConnectionStrings.GetOrDefault(connectionStringName); if (connStringInOptions != null) return connStringInOptions; return tenant.ConnectionStrings.Default ?? Options.ConnectionStrings.Default;
9、多租户的中间件
public async Task Invoke(HttpContext httpContext) //实现是遍历Cookie,Domain,Header,QueryString,Route不同ITenantResolveContributor //,获取Tenant的ID和Name var resolveResult = _tenantResolver.ResolveTenantIdOrName(); _tenantResolveResultAccessor.Result = resolveResult; //获取配置文件,包括字符串 TenantConfiguration tenant = null; if (resolveResult.TenantIdOrName != null) tenant = await FindTenantAsync(resolveResult.TenantIdOrName); if (tenant == null) //TODO: A better exception? throw new AbpException( "There is no tenant with given tenant id or name: " + resolveResult.TenantIdOrName ); //设置为当前租户 using (_currentTenant.Change(tenant?.Id, tenant?.Name)) await _next(httpContext);
以上是关于租户功能的主要内容,如果未能解决你的问题,请参考以下文章