hello,好久不见,大家好,欢迎来到橙子老哥的分享时刻,希望大家一起学习,一起进步。
欢迎加入.net意社区,第一时间了解我们的动态,文章第一时间分享至社区
社区官方地址:https://ccnetcore.com
(上千.neter聚集地)(注册人数:1691人)
官方微信公众号:公众号搜索 意.Net
官方微信小程序:小程序搜索 意.Net
添加橙子老哥
微信加入官方微信群:chengzilaoge520
1、引言
首先在这里,非常感谢薛家明
前辈,本章分享的内容,就是他的开源巨作
sharding-core:https://github.com/dotnetcore/sharding-core
我很佩服的他的愿意有三个:
- sharding-core已被NCC收编,足以证明项目的质量,
薛家明
先生一直在主力维护着,为EFCore在分表分库下提供一个成熟的解决方案 薛家明
先生撰写了非常多的有深度的文章博客,每一篇都可以学到很多东西,可以看出,是真心把知识分享了出去,但是本人却非常低调薛家明
先生不仅仅局限于.net,在java领域,同样开源了一个重量级优雅ORM项目:easy-query,设计方式与我想法不谋而同
献给转java的c#和java程序员的数据库orm框架
easy-query:https://github.com/dromara/easy-query
另外附上shardingcore的官方qq群:771630778
,非常推荐大家加入,可以学习很多,备注橙子老哥推荐进群
本篇文章,比较硬核,就一起来探究下ShardingCore
的源码吧
2、入口
众所皆知,efcore的代码出了名的难啃,不光量大,而且东西也特别抽象
对应的shardingcore的内容也非常非常多,而且很多地方借鉴了efcore的设计,所以阅读起来会比较有难度
本章只是对它的执行流程做一个大概的解读,不会精细到每一行
不过知道了大概的流程原理,等想看的时候或者出问题的时候,再去排查也是一个不错的选择
ShardingCore 一款efcore下高性能、轻量级针对分表分库读写分离的解决方案,具有零依赖、零学习成本、零业务代码入侵。
既然是基于EFcore的零业务代码入侵,那肯定要对Efcore进行扩展替换,我们直接从入口找找
在ShardingCoreExtension中,我们新增了shardingcode的服务,这里做了大量的依赖注入,其中UseDefaultSharding
是对EFCORE的扩展
services.AddDbContext<TShardingDbContext>(UseDefaultSharding<TShardingDbContext>, contextLifetime,
optionsLifetime);
最终,我们执行到:
public static DbContextOptionsBuilder UseSharding(
this DbContextOptionsBuilder optionsBuilder, IShardingRuntimeContext shardingRuntimeContext)
{
return optionsBuilder
.UseShardingWrapMark()
.UseShardingMigrator()
.UseShardingOptions(shardingRuntimeContext)
.ReplaceService<IQueryCompiler, ShardingQueryCompiler>()
.ReplaceService<IDbSetInitializer, ShardingDbSetInitializer>()
.ReplaceService<IChangeTrackerFactory, ShardingChangeTrackerFactory>()
.ReplaceService<IDbContextTransactionManager,ShardingRelationalTransactionManager>()
.ReplaceService<IStateManager,ShardingStateManager>()
.ReplaceService<IRelationalTransactionFactory,ShardingRelationalTransactionFactory>();
}
这里可以看到,替换了EfCore的非常多的服务,而今天,我们讲最核心的对象:IQueryCompiler
这个对象,是Efcore查询进行编译执行的内容,所以我们想知道具体shardingcode在查询的时候执行了什么,就看看ShardingQueryCompiler
3、ShardingQueryCompiler
EfCore对外提供了,IQueryCompiler接口,ShardingQueryCompiler
是他的实现
所以,请记住,无论做了什么,最终返回的结果,就是为了Execute方法返回的
public interface IQueryCompiler
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
TResult Execute<TResult>(Expression query);
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
TResult ExecuteAsync<TResult>(Expression query, CancellationToken cancellationToken);
ShardingQueryCompiler包了一层,我们继续:
private readonly IShardingCompilerExecutor _shardingCompilerExecutor;
public override TResult Execute<TResult>(Expression query)
{
return _shardingCompilerExecutor.Execute<TResult>(_shardingDbContext, query);
}
看看IShardingCompilerExecutor
public TResult Execute<TResult>(IShardingDbContext shardingDbContext, Expression query)
{
//预解析表达式,通过ShardingQueryPrepareVisitor解析Expression query
var prepareParseResult = _prepareParser.Parse(shardingDbContext,query);
_logger.LogDebug($"compile parameter:{
prepareParseResult}");
using (new CustomerQueryScope(prepareParseResult,_shardingRouteManager))
{
//根据上面预解析表达式,获取出上下文
//这里可以解析出QueryCompilerContext、MergeQueryCompilerContext两种类型
//后面会根据类型的不一样,选择对应的处理逻辑
var queryCompilerContext = _queryCompilerContextFactory.Create(prepareParseResult);
//再包一层,IShardingTrackQueryExecutor,把解析的上下文给了它
return _shardingTrackQueryExecutor.Execute<TResult>(queryCompilerContext);
}
}
4、IShardingTrackQueryExecutor
上面,我们可以看到,将解析的结果上下文传入IShardingTrackQueryExecutor,它的实现是:DefaultShardingTrackQueryExecutor,我们看看做了什么
public TResult Execute<TResult>(IQueryCompilerContext queryCompilerContext)
{
var queryCompilerExecutor = queryCompilerContext.GetQueryCompilerExecutor();
if (queryCompilerExecutor == null)
{
//上下文类型是需要合并的
if (queryCompilerContext is IMergeQueryCompilerContext mergeQueryCompilerContext)
{
//走IShardingQueryExecutor的处理逻辑
return _shardingQueryExecutor.Execute<TResult>(mergeQueryCompilerContext);
}
throw new ShardingCoreNotFoundException(queryCompilerContext.GetQueryExpression().ShardingPrint());
}
//如果有原生的queryCompilerExecutor,直接走原生的EFCORE,不需要走shardingcode了
//由于上面有return,上面的逻辑和下面的逻辑只会走一个
//native query
var result = queryCompilerExecutor.GetQueryCompiler().Execute<TResult>(queryCompilerExecutor.GetReplaceQueryExpression());
//native query track
return ResultTrackExecute(result, queryCompilerContext, TrackEnumerable, Track);
}
到这里,我们需要处理的合并表的逻辑,就要看IShardingTrackQueryExecutor
内,做了什么
public TResult