Abp框架多租戶源碼解讀及自定義拓展


1、Abp源碼解析

多租戶連接字符串處理類(EntityFrameworkCore版本),命名空間為Abp.Zero.EntityFrameworkCore

/// <summary>
/// Implements <see cref="IDbPerTenantConnectionStringResolver"/> to dynamically resolve
/// connection string for a multi tenant application.
/// </summary>
public class DbPerTenantConnectionStringResolver : DefaultConnectionStringResolver, IDbPerTenantConnectionStringResolver

入口方法:

public override string GetNameOrConnectionString(ConnectionStringResolveArgs args)
{
	if (args.MultiTenancySide == MultiTenancySides.Host)
    {
    	return GetNameOrConnectionString(new DbPerTenantConnectionStringResolveArgs(null, args));
    }

    return GetNameOrConnectionString(new 		                                                             DbPerTenantConnectionStringResolveArgs(GetCurrentTenantId(), args));
}
public class ConnectionStringResolveArgs : Dictionary<string, object>
{
    public MultiTenancySides? MultiTenancySide { get; set; } // 表示當前是租戶還是Host

    public ConnectionStringResolveArgs(MultiTenancySides? multiTenancySide = null)
    {
    	MultiTenancySide = multiTenancySide;
    }
}

什么時候調用入口方法?初始化DbContext的時候。如iRepository.GetAllList()

這個方法做了什么事情?

public virtual string GetNameOrConnectionString(DbPerTenantConnectionStringResolveArgs args)
{
    if (args.TenantId == null)
    {
        //Host取默認的連接字符串
        return base.GetNameOrConnectionString(args);
    }

    //從緩存中取值
	var tenantCacheItem = _tenantCache.Get(args.TenantId.Value);
    if (tenantCacheItem.ConnectionString.IsNullOrEmpty())
    {
        //租戶沒配置連接字符串,則返回默認的連接字符串
        return base.GetNameOrConnectionString(args);
    }

	return tenantCacheItem.ConnectionString;
}

看取Host的連接字符串方法:

public virtual string GetNameOrConnectionString(ConnectionStringResolveArgs args)
{
	Check.NotNull(args, nameof(args));

    var defaultConnectionString = _configuration.DefaultNameOrConnectionString;
    if (!string.IsNullOrWhiteSpace(defaultConnectionString))
    {
    	return defaultConnectionString;
    }

    if (ConfigurationManager.ConnectionStrings["Default"] != null)
    {
    	return "Default";
    }

    if (ConfigurationManager.ConnectionStrings.Count == 1)
    {
    	return ConfigurationManager.ConnectionStrings[0].ConnectionString;
    }

    throw new AbpException("Could not find a connection string definition for the application. Set IAbpStartupConfiguration.DefaultNameOrConnectionString or add a 'Default' connection string to application .config file.");
}

問題來了,_tenantCache中哪來的租戶配置?看這個Get方法:

public virtual TenantCacheItem Get(int tenantId)
{
    var cacheItem = GetOrNull(tenantId);

    if (cacheItem == null)
    {
    	throw new AbpException("There is no tenant with given id: " + tenantId);
    }

    return cacheItem;
}

繼續看GetOrNull方法:

public TenantCacheItem GetOrNull(int tenantId)
{
    return _cacheManager
    	.GetTenantCache()
    	.Get(
            tenantId,
            () =>
            {
                var tenant = GetTenantOrNull(tenantId);
                if (tenant == null)
                {
                	return null;
                }

            return CreateTenantCacheItem(tenant);
            }
    	);
}

這個其實就是從緩存中取值,並做了補全功能。繼續,看GetTenantOrNull方法:

[UnitOfWork]
protected virtual TTenant GetTenantOrNull(int tenantId)
{
    using (_unitOfWorkManager.Current.SetTenantId(null))
    {
    	return _tenantRepository.FirstOrDefault(tenantId);
    }
}

到這里,其實要去租戶配置表中查詢該租戶配置的信息,所以調用了方法_unitOfWorkManager.Current.SetTenantId(null),調用了方法以后,代碼執行到_tenantRepository.FirstOrDefault(tenantId)時,會重新調用獲取連接字符串的方法,即我們說的入口方法。

最后,拿到Host的數據庫連接字符串,_tenantRepository.FirstOrDefault(tenantId)查詢當前租戶對應的連接字符串。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM