(好雨知時節,大雨 _ _ _)
時不時的呢,會有小伙伴問我這樣的問題:
1、群主,你的.tsv文件是如何生成的?
2、在線項目數據和種子數據的不一樣,可以下么?
3、如果我本地的數據開發好了,如何把新的數據遷到生產環境呢?
01
PART
設計思路
這幾個問題還是問了一段時間后,我感覺是時候需要考慮考慮了,之前一直比較懶或者沒有很好的辦法去處理這個問題,其實今天的辦法也不是最完美的,所以我叫思路一,如果有好的思路歡迎留言和建議,有獎勵喲。
今天就暫時先說說這個簡單的方案吧,比較簡單,就是把數據從一個DB,遷到另一個DB,然后增加一個輸出tsv的功能,看似很簡單,還是用到了一些知識點的:
1、多表聯合,這個是基礎,任何ORM都支持;
2、讀寫分離,但是有2個前提,下文會具體說;
3、事務處理,保證數據一致性嘛;
那下邊就具體說說,如何來實現。
02
PART
開發流程
代碼不是很多,相信一遍就能看懂。
1、獲取集合內完整數據
這里用到了多表聯合查詢,畢竟SqlSugar不像EFCore那樣,可以一次性就把子屬性給全部查詢出來,感覺就像聚合一樣,那在SqlSugar中的寫法有兩種,官方默認的是Mapper好一些:
/// <summary>
/// 查詢出角色-菜單-接口關系表全部Map屬性數據
/// </summary>
/// <returns></returns>
public async Task<List<RoleModulePermission>> GetRMPMaps()
{return await Db.Queryable<RoleModulePermission>().Mapper(rmp => rmp.Module, rmp => rmp.ModuleId).Mapper(rmp => rmp.Permission, rmp => rmp.PermissionId).Mapper(rmp => rmp.Role, rmp => rmp.RoleId).Where(d => d.IsDeleted == false).ToListAsync();
}
PS:這里我不想再討論各種ORM的孰優孰劣了,那是小孩紙才會干的事兒,我項目EFCore也用,Dapper也會,就醬吧。
2、開啟數據庫讀寫分離模式
既然要數據庫遷移,肯定是需要一個DB轉移到另一個DB,因為我們的項目正好已經實現了讀寫分離模式,那正好利用這個機制,主庫為寫,所以配置為新庫,從庫為讀,所以配置為舊庫。結果是這樣的:
這里要注意四點:
1、既然要遷移數據,那新庫只生成表結構就行,不用初始化數據,False;
2、設置主庫的ConnID;
3、開啟CQRSEnabled開關,并配置主從庫地址;
4、主從庫數據庫類型一致,不然會報錯,畢竟不是多庫模式;
千萬記得新庫是用來寫的,所以是主庫。
那最后啟動項目結果是這樣的:
3、開始遷移
萬事俱備,只欠東風了,這一步就是要遷移數據邏輯了。其實整個項目核心的就是權限聚合部分了,涉及到了四個表:
角色表、菜單表、接口表、關系表。
因為系統用的是整型的自增主鍵ID,所以要考慮好關系表中,rid、mid、pid的值,要與對應表的id是一致的,如果你一直用的的GUID字符串的話,就不用考慮這個問題,無腦的數據遷移就行.
那現在要保證關系表的id問題,我是這么寫的,在MigrateController.cs中:
 /// <summary>/// 獲取權限部分Map數據(從庫)/// 遷移到新庫(主庫)/// </summary>/// <returns></returns>[HttpGet]public async Task<MessageModel<string>> DataMigrateFromOld2New(){var data = new MessageModel<string>() { success = true, msg = "" };if (_env.IsDevelopment()){try{// 獲取權限集合數據 var rmps = await _roleModulePermissionServices.GetRMPMaps();// 當然,你可以做個where查詢//rmps = rmps.Where(d => d.ModuleId > 88).ToList();// 開啟事務,保證數據一致性_unitOfWork.BeginTran();var rid = 0;var pid = 0;var mid = 0;var rpmid = 0;// 注意信息的完整性,不要重復添加,確保主庫沒有要添加的數據foreach (var item in rmps){// 角色信息,防止重復添加,做了判斷if (item.Role != null){var isExit = (await _roleServices.Query(d => d.Name == item.Role.Name && d.IsDeleted == false)).FirstOrDefault();if (isExit == null){rid = await _roleServices.Add(item.Role);Console.WriteLine($"Role Added:{item.Role.Name}");}else{rid = isExit.Id;}}// 菜單if (item.Permission != null){pid = await _permissionServices.Add(item.Permission);Console.WriteLine($"Permission Added:{item.Permission.Name}");}// 接口if (item.Module != null){mid = await _moduleServices.Add(item.Module);Console.WriteLine($"Module Added:{item.Module.LinkUrl}");}// 關系if (rid > 0 && pid > 0 && mid > 0){rpmid = await _roleModulePermissionServices.Add(new RoleModulePermission(){IsDeleted = false,CreateTime = DateTime.Now,ModifyTime = DateTime.Now,ModuleId = mid,PermissionId = pid,RoleId = rid,});Console.WriteLine($"RMP Added:{rpmid}");}}_unitOfWork.CommitTran();data.success = true;data.msg = "導入成功!";}catch (Exception){_unitOfWork.RollbackTran();}}else{data.success = false;data.msg = "當前不處于開發模式,代碼生成不可用!";}return data;}
邏輯很簡單,就是獲取到整體數據后,一個個添加到新庫里,然后再添加關系表,保證數據的完整性,然后用事務,如果出錯,可以回滾,保證一致性。
4、查看結果
到了這里,基本就沒有問題了,可以看到數據已經完成了遷移:
(遷移過程,輸出到控制臺)
(數據庫查看新庫,已經有了數據)
這里完全不用膽小你的生產數據庫是否已經有數據了,無論有沒有,添加的權限關系表的id,也一定會和三個子表是一一對應的,且id自增,沒問題。
關于其他用戶表,博客表肯定不需要遷移吧,這些本地環境肯定是沒有的。
那遷移完了數據,如何生成到tsv文件里呢,請往下看。
03
PART
輸出到文件
那現在我們的新庫有了數據,我們就可以切換到單庫模式來從新庫里獲取數據,然后生成到tsv文件里
 [HttpGet]public async Task<MessageModel<string>> SaveData2TsvAsync(){var data = new MessageModel<string>() { success = true, msg = "" };if (_env.IsDevelopment()){try{// 取出數據,序列化,自己可以處理判空var rolesJson = JsonConvert.SerializeObject(await _roleServices.Query(d => d.IsDeleted == false));FileHelper.WriteFile(Path.Combine(_env.WebRootPath, "BlogCore.Data.json", "Role_New.tsv"), rolesJson, Encoding.UTF8);var permissionsJson = JsonConvert.SerializeObject(await _permissionServices.Query(d => d.IsDeleted == false));FileHelper.WriteFile(Path.Combine(_env.WebRootPath, "BlogCore.Data.json", "Permission_New.tsv"), permissionsJson, Encoding.UTF8);var modulesJson = JsonConvert.SerializeObject(await _moduleServices.Query(d => d.IsDeleted == false));FileHelper.WriteFile(Path.Combine(_env.WebRootPath, "BlogCore.Data.json", "Modules_New.tsv"), modulesJson, Encoding.UTF8);var rmpsJson = JsonConvert.SerializeObject(await _roleModulePermissionServices.Query(d => d.IsDeleted == false));FileHelper.WriteFile(Path.Combine(_env.WebRootPath, "BlogCore.Data.json", "RoleModulePermission_New.tsv"), rmpsJson, Encoding.UTF8);}catch (Exception){}data.success = true;data.msg = "生成成功!";}else{data.success = false;data.msg = "當前不處于開發模式,代碼生成不可用!";}return data;}
結果我就不展示了,自己試試就可以了。
思考與總結
從上邊的代碼中,我們可以看出來,因為框架已經集成了很多重要的功能,比如讀寫分離和事務處理,所以代碼還是比較簡單的,如果自己從0開始寫,還是比較麻煩的。
現在還有一個問題需要思考下,如果實現不同類型數據庫的生成,這里也是兩種辦法:
1、使用框架的多庫模式,先從庫1獲取數據,然后切換數據庫,再生成到庫2;
2、可以生成到tsv文件里做個跳板,這不過這里有一個問題,就是關系表的id如果不一樣,一定會混亂的,所以這個時候又說到了主鍵用INT還是GUID的問題了,自己處理吧。
還是歡迎大家多多提意見吧,如何對業務數據進行同步遷移,是一個好課題。
                            總結
                            
                                以上是生活随笔為你收集整理的实现业务数据的同步迁移 · 思路一的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                            
                                如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。