日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

使用EntityFrameworkCore实现Repository, UnitOfWork,支持MySQL分库分表

發(fā)布時間:2023/12/4 数据库 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用EntityFrameworkCore实现Repository, UnitOfWork,支持MySQL分库分表 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

昨天(星期五)下班,19:00左右回到家,洗個澡,然后20:30左右開始寫代碼,寫完代碼之后,上床看了《生活大爆炸10季》17、18兩集,發(fā)現(xiàn)沒有更新到19集,瞄了一眼手機,竟然已經(jīng)是凌晨02:00多了,關掉電視睡覺,10:30左右被老婆電話吵醒,洗漱完畢,去麥當勞吃了一個早餐,然后屁顛屁顛地坐地鐵到很遠的地方去爬山。爬山回來之后,閑來無事,寫篇文章記錄一下昨晚所花的幾個小時干的事情——使用EntityFrameworkCore實現(xiàn)Repository<TEntity>,?UnitOfWork<TContext>,支持MySQL分庫分表。

由于是使用業(yè)余時間寫來玩的,時間也有限,所以,全部代碼做了一個基本假設:Repository<TEntity>,?UnitOfWork<TContext>只支持同一個IP上的MySQL分庫分表,不同IP上的MySQL分庫分表,需要使用不同的Repository<TEntity>,?UnitOfWork<TContext>對象。以下示例代碼,假設數(shù)據(jù)庫是按年分庫按月分表。

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分庫分表,首要條件是必須能做到可以動態(tài)地改變庫名表名。軟件界有一句老話叫:凡是做不到的就多抽象一層,所以,想要讓EntityFrameworkCore支持MySQL分庫分表,我抽象了以下兩個接口,?IRepository<TEntity>和IUnitOfWork

很多人都自己動手實現(xiàn)過Repository和UnitOfWork,雖然各自實現(xiàn)不盡相同,但是其實現(xiàn)本身并沒有難度,但在這里,我們需要特別關注兩個方法: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);

怎么實現(xiàn)這兩個方法,就需要一定的技術功底了,我以前在一家創(chuàng)業(yè)公司的時候,因為看不慣架構師自以為是的樣子,自己動手寫了一個輕量級的ORM框架,如果以后有時間,我打算寫一篇《如何基于Dapper實現(xiàn)一個輕量級的ORM框架》的文章。ORM框架背后的動機很單純,就是數(shù)據(jù)庫與Domain之間的一種雙向映射,真正把這種單純的動機搞復雜是的那些性能優(yōu)化,各種緩存實現(xiàn)。而從Domain到數(shù)據(jù)庫這一單方向上的映射,在.NET領域借助了一種代碼即數(shù)據(jù)的思想,再細化到C#語言代碼即數(shù)據(jù)就是表達式樹。所以,我們有理由相信:SQL是根據(jù)表達式樹生成的。現(xiàn)在我們已經(jīng)找準了方向,那么我們看看EntityFrameworkCore在什么地方生成表名的,也就是說,我們只需要修改一下生成表名的代碼,就可以做到動態(tài)生成database.table?SQL。EntityFrameworkCore是通過TableExpression來生成表名的:

public class TableExpression{public virtual string Table { get; }public virtual string Schema { get; }}

如果你MySQL知識至少跟我一樣的水平的話,看到TableExpression表達式有一個Schema是不是立即就可以想到:哈哈,太好了,我壓根就不用修改EntityFrameworkCore本身的代碼就可以實現(xiàn)。為什么呢?好吧,看看MySQL官網(wǎng)怎么說Schema的:

In MySQL, physically, a schema is synonymous with a database. You can substitute the keyword SCHEMA instead of DATABASE in MySQL SQL syntax, for example using CREATE SCHEMA instead of CREATE DATABASE. Some other database products draw a distinction. For example, in the Oracle Database product, a schema represents only a part of a database: the tables and other objects owned by a single user.

好吧,Schema就是Database,那么我們就用Schema.Table來表示database.table。現(xiàn)在事情就變得簡單了,變成了我們如何動態(tài)地改變Schema和Table了,以下是我提供的簡化實現(xiàn):

/// <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>public void ChangeDatabase(string database){if (_context.Model.Relational() is RelationalModelAnnotations relational){relational.DatabaseName = database;}var connection = _context.Database.GetDbConnection();if (connection.State.HasFlag(ConnectionState.Open)){connection.ChangeDatabase(database);}var items = _context.Model.GetEntityTypes();foreach (var item in items){if (item.Relational() is RelationalEntityTypeAnnotations extensions){extensions.Schema = 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>public void ChangeTable(string table){if (_dbContext.Model.FindEntityType(typeof(TEntity)).Relational() is RelationalEntityTypeAnnotations relational){relational.TableName = table;}}

OK, 雖然有點low,但是畢竟支持了MySQL分庫分表,看看怎么用:

namespace QuickStart.Controllers{[Route("api/[controller]")]public class UserController : ApiController{private readonly IUnitOfWork _unitOfWork;// 1. IRepositoryFactory used for readonly scenario; ? ? ? ?// 2. IUnitOfWork used for read/write scenario; ? ? ? ?// 3. IUnitOfWork<TContext> used for multiple databases scenario;public UserController(IUnitOfWork unitOfWork){_unitOfWork ?= unitOfWork;unitOfWork.ChangeDatabase($"rigofunc_{DateTime.Now.Year}");var userRepo = unitOfWork.GetRepository<User>();var postRepo = unitOfWork.GetRepository<Post>();var ym = DateTime.Now.ToString("yyyyMM");userRepo.ChangeTable($"t_user_{ym}");postRepo.ChangeTable($"t_post_{ym}");var user = new User{//UserId = 123, ? ? ? ? ? ? ? ?UserName = "rigofunc",Password = "password"};userRepo.Insert(user);var post = new Post{//PostId = 123, ? ? ? ? ? ? ? ?UserId = user.UserId,Content = "What a piece of junk!"};postRepo.Insert(post);unitOfWork.SaveChanges();var find = userRepo.Find(user.UserId);find.Password = "p@ssword";unitOfWork.SaveChanges();}[HttpGet]public IPagedList<User> Get(){_unitOfWork.ChangeDatabase($"rigofunc_2018");var userRepo = _unitOfWork.GetRepository<User>();return userRepo.Query(u => true).OrderBy(u => u.UserId).ToPagedList(0, 20);}}}

以下是生成的SQL:

? ? ?Executed DbCommand [Parameters=[@p2='?', @p4='?' (Size = 8000), @p6='?' (Size = 8000)], CommandType='Text', CommandTimeout='0']INSERT INTO `rigofunc_2017`.`t_user_201703` (`Fis_deleted`, `Fpassword`, `Fname`)VALUES (@p2, @p4, @p6);SELECT LAST_INSERT_ID();Executed DbCommand [Parameters=[@p10='?' (Size = 8000), @p12='?', @p14='?'], CommandType='Text', CommandTimeout='0']INSERT INTO `rigofunc_2017`.`t_post_201703` (`Fcontent`, `Fis_deleted`, `Fuser_id`)VALUES (@p10, @p12, @p14);SELECT LAST_INSERT_ID();Executed DbCommand [Parameters=[@p0='?', @p3='?', @p4='?' (Size = 8000)], CommandType='Text', CommandTimeout='0']UPDATE `rigofunc_2017`.`t_user_201703` SET `Fpassword` = @p4WHERE `Fid` = @p0 AND `Fis_deleted` = @p3;SELECT ROW_COUNT();Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='0']SELECT `u`.`Fid`, `u`.`Fis_deleted`, `u`.`Fpassword`, `u`.`Fname`FROM `rigofunc_2017`.`t_user_201703` AS `u`ORDER BY `u`.`Fid`Executed DbCommand (2ms) [Parameters=[], CommandType='Text', CommandTimeout='0']SELECT `u`.`Fid`, `u`.`Fis_deleted`, `u`.`Fpassword`, `u`.`Fname`FROM `rigofunc_2018`.`t_user_201703` AS `u`ORDER BY `u`.`Fid`

ABOUT ME:

I’m a software architect, particularly love .NET Core, but I also embrace all the new stuff. I’m on GitHub with?xyting, and my packages publish on NuGet with?rigofunc

原文地址:http://www.xyting.org/2017/03/25/EFCore-Support-MySQL-Multiple-Databases-And-Tables-sharding.html


.NET社區(qū)新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關注

總結

以上是生活随笔為你收集整理的使用EntityFrameworkCore实现Repository, UnitOfWork,支持MySQL分库分表的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。