租户功能

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);
            }
        }

  

转载于:https://www.cnblogs.com/cloudsu/p/11169211.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值