昨天(星期五)下班,19:00左右回到家,洗个澡,然后20:30左右开始写代码,写完代码之后,上床看了《生活大爆炸10季》17、18两集,发现没有更新到19集,瞄了一眼手机,竟然已经是凌晨02:00多了,关掉电视睡觉,10:30左右被老婆电话吵醒,洗漱完毕,去麦当劳吃了一个早餐,然后屁颠屁颠地坐地铁到很远的地方去爬山。爬山回来之后,闲来无事,写篇文章记录一下昨晚所花的几个小时干的事情——使用EntityFrameworkCore实现Repository<TEntity>
, UnitOfWork<TContext>
,支持MySQL分库分表。
由于是使用业余时间写来玩的,时间也有限,所以,全部代码做了一个基本假设:Repository<TEntity>
, UnitOfWork<TContext>
只支持同一个IP上的MySQL分库分表,不同IP上的MySQL分库分表,需要使用不同的Repository<TEntity>
, UnitOfWork<TContext>
对象。以下示例代码,假设数据库是按年分库按月分表。
EntityFrameworkCore
默认并不支持分库分表,我们看一眼EntityFrameworkCore
默认生成的SQL:
Executed DbCommand [Parameters=[@p2='?', @p4='?' (Size = 8000), @p6='?' (Size = 8000)], CommandType='Text', CommandTimeout='0']INSERT INTO `t_user_201703` (`Fis_deleted`, `Fpassword`, `Fname`)VALUES (@p2, @p4, @p6);SELECT LAST_INSERT_ID();
默认生成的SQL并没有带上库名,而想要让EntityFrameworkCore
支持MySQL分库分表,首要条件是必须能做到可以动态地改变库名表名。软件界有一句老话叫:凡是做不到的就多抽象一层,所以,想要让EntityFrameworkCore
支持MySQL分库分表,我抽象了以下两个接口, IRepository<TEntity>
和IUnitOfWork
很多人都自己动手实现过Repository
和UnitOfWork
,虽然各自实现不尽相同,但是其实现本身并没有难度,但在这里,我们需要特别关注两个方法:void ChangeTable(string table)
和void ChangeDatabase(string database)
/// <summary> /// Changes the table name. This require the tables in the same database. /// </summary> /// <param name="table"></param> /// <remarks> /// This only been used for supporting multiple tables in the same model. This require the tables in the same database. /// </remarks>
void ChangeTable(string table);
/// <summary> /// Changes the database name. This require the databases in the same machine. /// </summary> /// <param name="database">The database name.</param> /// <remarks> /// This only been used for supporting multiple databases in the same model. This require the databases in the same machine. /// </remarks>
void ChangeDatabase(string database);
怎么实现这两个方法,就需要一定的技术功底了,我以前在一家创业公司的时候,因为看不惯架构师自以为是的样子,自己动手写了一个轻量级的ORM框架,如果以后有时间,我打算写一篇《如何基于Dapper实现一个轻量级的ORM框架》的文章。ORM框架背后的动机很单纯,就是数据库与Domain之间的一种双向映射,真正把这种单纯的动机搞复杂是的那些性能优化,各种缓存实现。而从Domain到数据库这一单方向上的映射,在.NET领域借助了一种代码即数据的思想,再细化到C#语言代码即数据就是表达式树。所以,我们有理由相信:SQL是根据表达式树生成的。现在我们已经找准了方向,那么我们看看EntityFrameworkCore
在什么地方生成表名的,也就是说,我们只需要修改一下生成表名的代码,就可以做到动态生成database
.table
SQL。EntityFrameworkCore
是通过TableExpression
来生成表名的:
public class TableExpression{
public virtual string Table {
get; }
public virtual string Schema {
get; }}