mysql读写分离 abp_企业级工作流解决方案(十三)--集成Abp和ng-alain--数据库读写分离...

说到程序里面数据库管理,无非就是两件事情,一是数据库操作,对于数据库的操作,各种程序语言都有封装,也就是所谓的ORM框架,.net 方向一般用得比较多和就是.net framework和dapper,abp里还集成了NHibernate,另外就是连接字符串的管理,简单的应用直接用一个数据库连接字符串就可以了,但是对于大型的应用,比如有多租户概念的系统,比如有一些分库分表需求的设计系统,那么连接字符串的管理将是非常复杂和核心的内容。

对于读写分离,大家应该比较熟悉,数据库层面,大型的关系型数据库都支持,这里的读写分离是指代码层面,针对DBA已经做好的数据库读写分离来管理数据库连接字符串。

Abp基本框架提供了最基础的数据库连接字符串管理,zero项目实现了多租户的数据库连接管理,即把每个租户的连接字符串存储在租户里面,对于每一个Uow操作,都会找租户的连接字符串,如果找到,就使用,没有找到,向上层找默认的连接字符串。代码如下:

///

///Implementsto dynamically resolve///connection string for a multi tenant application.///

public classDbPerTenantConnectionStringResolver : DefaultConnectionStringResolver, IDbPerTenantConnectionStringResolver

{///

///Reference to the session.///

public IAbpSession AbpSession { get; set; }private readonlyICurrentUnitOfWorkProvider _currentUnitOfWorkProvider;private readonlyITenantCache _tenantCache;///

///Initializes a new instance of theclass.///

publicDbPerTenantConnectionStringResolver(

IAbpStartupConfiguration configuration,

ICurrentUnitOfWorkProvider currentUnitOfWorkProvider,

ITenantCache tenantCache)

:base(configuration)

{

_currentUnitOfWorkProvider=currentUnitOfWorkProvider;

_tenantCache=tenantCache;

AbpSession=NullAbpSession.Instance;

}public override stringGetNameOrConnectionString(ConnectionStringResolveArgs args)

{if (args.MultiTenancySide ==MultiTenancySides.Host)

{return GetNameOrConnectionString(new DbPerTenantConnectionStringResolveArgs(null, args));

}return GetNameOrConnectionString(newDbPerTenantConnectionStringResolveArgs(GetCurrentTenantId(), args));

}public virtual stringGetNameOrConnectionString(DbPerTenantConnectionStringResolveArgs args)

{if (args.TenantId == null)

{//Requested for host

return base.GetNameOrConnectionString(args);

}var tenantCacheItem =_tenantCache.Get(args.TenantId.Value);if(tenantCacheItem.ConnectionString.IsNullOrEmpty())

{//Tenant has not dedicated database

return base.GetNameOrConnectionString(args);

}returntenantCacheItem.ConnectionString;

}protected virtual int?GetCurrentTenantId()

{return _currentUnitOfWorkProvider.Current != null

?_currentUnitOfWorkProvider.Current.GetTenantId()

: AbpSession.TenantId;

}

}

那么我们要改造的地方其他就可以参照这个来管理连接字符串

还有一个问题,就是我们怎么让框架知道我们使用的是读库还是写库呢?

Abp里面,公开给用户控制Uow的,就是UnitOfWorkAttribute装饰器,增加一个读库还是写库的标识IsReadDb,在UnitOfWorkOptions类里面也要加对应的属性,那么我们在构造UnitOfWorkOptions类的时候,可以把属性装饰器里面的IsReadDb属性赋值给UnitOfWorkOptions,再获取DbContext方法的时候,把此参数传入Uow连接字符串管理,在连接字符串管理里面,判断此参数的值,确定数据库字符串选择。

主要代码:

///

///Unit of work options.///

public classUnitOfWorkOptions

{//......

///

///自定义:设置是否是读库///

public bool IsReadDb { get; set; }

}

[AttributeUsage(AttributeTargets.Method| AttributeTargets.Class |AttributeTargets.Interface)]public classUnitOfWorkAttribute : Attribute

{//......

///

///自定义:设置是否是读库///

public bool IsReadDb { get; set; }publicUnitOfWorkOptions CreateOptions()

{return newUnitOfWorkOptions

{

IsTransactional=IsTransactional,

IsolationLevel=IsolationLevel,

Timeout=Timeout,

Scope=Scope,

IsReadDb=IsReadDb

};

}

}

在获取DbContext方法的时候,传递数据库读写参数

public static classUnitOfWorkExtensions

{public static TDbContext GetDbContext(this IActiveUnitOfWork unitOfWork, MultiTenancySides? multiTenancySide = null, string name = null)whereTDbContext : DbContext

{if (unitOfWork == null)

{throw new ArgumentNullException("unitOfWork");

}if (!(unitOfWork isEfCoreUnitOfWork))

{throw new ArgumentException("unitOfWork is not type of" + typeof(EfCoreUnitOfWork).FullName, "unitOfWork");

}return (unitOfWork as EfCoreUnitOfWork).GetOrCreateDbContext(multiTenancySide, name, unitOfWork.Options.IsReadDb);

}

}public virtual TDbContext GetOrCreateDbContext(MultiTenancySides? multiTenancySide = null, string name = null,bool isReadDb = false)whereTDbContext : DbContext

{var concreteDbContextType = _dbContextTypeMatcher.GetConcreteType(typeof(TDbContext));var connectionStringResolveArgs = newConnectionStringResolveArgs(multiTenancySide);

connectionStringResolveArgs["DbContextType"] = typeof(TDbContext);

connectionStringResolveArgs["DbContextConcreteType"] =concreteDbContextType;

connectionStringResolveArgs["IsReadDb"] =isReadDb;var connectionString =ResolveConnectionString(connectionStringResolveArgs);

}

最终,参照Abp连接字符串的管理,代码如下:

public classDbPerTenantConnectionStringResolver : DefaultConnectionStringResolver, IDbPerTenantConnectionStringResolver

{///

///Reference to the session.///

public IAbpSession AbpSession { get; set; }private readonlyICurrentUnitOfWorkProvider _currentUnitOfWorkProvider;///

///Initializes a new instance of theclass.///

publicDbPerTenantConnectionStringResolver(

IAbpStartupConfiguration configuration,

ICurrentUnitOfWorkProvider currentUnitOfWorkProvider)

:base(

configuration)

{

_currentUnitOfWorkProvider=currentUnitOfWorkProvider;

AbpSession=NullAbpSession.Instance;

}public override stringGetNameOrConnectionString(ConnectionStringResolveArgs args)

{if (args.MultiTenancySide ==MultiTenancySides.Host)

{return GetNameOrConnectionString(new DbPerTenantConnectionStringResolveArgs(null, args));

}return GetNameOrConnectionString(newDbPerTenantConnectionStringResolveArgs(GetCurrentTenantId(), args));

}public virtual stringGetNameOrConnectionString(DbPerTenantConnectionStringResolveArgs args)

{if (args.TenantId == null)

{//Requested for host

return base.GetNameOrConnectionString(args);

}var tenantCacheItem = Rpc.Call("CommonService.CommonServiceServiceAppService.GetTenantInfo", args.TenantId);if(tenantCacheItem == null)

{return base.GetNameOrConnectionString(args);

}if(Convert.ToBoolean(args["IsReadDb"]))

{if(!string.IsNullOrEmpty(tenantCacheItem.ReadConnectionString))

{returntenantCacheItem.ReadConnectionString;

}else{return base.GetNameOrConnectionString(args);

}

}else{if (!string.IsNullOrEmpty(tenantCacheItem.ConnectionString))

{returntenantCacheItem.ConnectionString;

}else{return base.GetNameOrConnectionString(args);

}

}

}protected virtual int?GetCurrentTenantId()

{return _currentUnitOfWorkProvider.Current != null

?_currentUnitOfWorkProvider.Current.GetTenantId()

: AbpSession.TenantId;

}

}

使用的时候,在类或者方法上,增加Uow属性装饰器上定义参数即可

补充说明:可以参照这种方式,自定义的扩展,比如每一个DbContext自定义连接字符串,我们可以在自己的租户管理表中添加属性,自定义数据库连接字符串选择逻辑。

原文:https://www.cnblogs.com/spritekuang/p/10819375.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值