Base solution for your next web application
Open Closed

Multi Tenancy subdomains #2310


User avatar
0
oguzhanacikgoez created

Hi,

at first i have to say "Great Work!" to Halil. You have built the most genius architecture I have seen before, thank you for that. In my application I created multiple tenants for my customers. Currently they're log-in with the default form and need to put the tenant name in the input field. For a better user experience I target to detect the tenant by requested URL. customer1.myapp.com -> runs the application with the customer1 database

How could i solve this issue, any idea?


1 Answer(s)
  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    First of all thanks :).

    You need to create a new session of yours like this.

    public class AspNetZeroAbpSession : ClaimsAbpSession
    {
        public ITenantIdAccessor TenantIdAccessor { get; set; }
    
        public override int? TenantId
        {
            get
            {
                if (base.TenantId != null)
                {
                    return base.TenantId;
                }
    
                //Try to find tenant from ITenantIdAccessor, if provided
                return TenantIdAccessor?.GetCurrentTenantIdOrNull(false); //set false to prevent circular usage.
            }
        }
    
        public AspNetZeroAbpSession(IMultiTenancyConfig multiTenancy) 
            : base(multiTenancy)
        {
    
        }
    }
    
    public class TenantIdAccessor : ITenantIdAccessor, ISingletonDependency
    {
        private const string HttpContextKey = "AbpZeroCurrentTenantCacheItem";
    
        private readonly ITenantCache _tenantCache;
        private readonly IIocResolver _iocResolver;
    
        private readonly AsyncLocal<int?> _currentTenant;
        private readonly Lazy<IAbpSession> _abpSession;
        private readonly Lazy<ITenancyNameFinder> _tenancyNameFinder;
    
        private int? CurrentTenantId
        {
            get
            {
                if (HttpContext.Current != null)
                {
                    _currentTenant.Value = HttpContext.Current.Items[HttpContextKey] as int?;
                    return _currentTenant.Value;
                }
    
                return _currentTenant.Value;
            }
    
            set
            {
                _currentTenant.Value = value;
                if (HttpContext.Current != null)
                {
                    HttpContext.Current.Items[HttpContextKey] = _currentTenant.Value;
                }
            }
        }
    
        public TenantIdAccessor(
            ITenantCache tenantCache,
            IIocResolver iocResolver)
        {
            _tenantCache = tenantCache;
            _iocResolver = iocResolver;
    
            _currentTenant = new AsyncLocal<int?>();
            _abpSession = new Lazy<IAbpSession>(() => _iocResolver.Resolve<IAbpSession>(), true);
            _tenancyNameFinder = new Lazy<ITenancyNameFinder>(() => _iocResolver.Resolve<ITenancyNameFinder>(), true);
        }
    
        /// <summary>
        /// Gets current tenant id.
        /// Use <see cref="IAbpSession.TenantId"/> wherever possible (if user logged in).
        /// This method tries to get current tenant id even if current user did not log in.
        /// </summary>
        /// <param name="useSession">Set false to skip session usage</param>
        public int? GetCurrentTenantIdOrNull(bool useSession = true)
        {
            if (useSession)
            {
                return _abpSession.Value.TenantId;
            }
    
            return CurrentTenantId;
        }
    
        /// <summary>
        /// This method is called on request beginning to obtain current tenant id.
        /// </summary>
        public void SetCurrentTenantId()
        {
            var tenancyName = _tenancyNameFinder.Value.GetCurrentTenancyNameOrNull();
            if (tenancyName == null)
            {
                CurrentTenantId = null;
                return;
            }
    
            CurrentTenantId = _tenantCache.GetOrNull(tenancyName)?.Id;
        }
    }
    

    Then replace IAbpSession with your session on your core module's PreInitialize method.

    Configuration.ReplaceService<IAbpSession, AspNetZeroAbpSession>();
    

    Finally, use below code in your Global.asax's Application_AuthenticateRequest.

    if (User?.Identity != null && User.Identity.IsAuthenticated)
    {
        return;
    }
    
    using (var currentTenantAccessor = AbpBootstrapper.IocManager.ResolveAsDisposable<TenantIdAccessor>())
    {
        currentTenantAccessor.Object.SetCurrentTenantId();
    }