企業專案實戰 .Net Core + Vue/Angular 分庫分表日誌系統 | 簡單的分庫分表設計
阿新 • • 發佈:2020-08-17
#前言
專案涉及到了一些設計模式,如果你看的不是很明白,沒有關係堅持下來,寫完之後去思考去品,你就會有一種突撥開雲霧的感覺,所以請不要在半途感覺自己看不懂選擇放棄,如果我哪裡寫的詳細,或者需要修正請聯絡我,謝謝。
#建立專案
##1.SDK安裝
我們開發用的vs版本是2019 .Net Core的版本是3.1
下載 SDK 地址 :https://dotnet.microsoft.com/download
![](https://git.imweb.io/hdong/ImageBed/raw/master/EasyLoggerImages/CoreSDKDown.png)
##2.新建專案
![](https://git.imweb.io/hdong/ImageBed/raw/master/EasyLoggerImages/Create1.png)
![](https://images.cnblogs.com/cnblogs_com/HDONG/1827694/o_200813143420CreateApi.png)
這裡選擇Core 版本是3.1 專案型別是API
Docker支援我們不勾選,我會在後續給大家單獨再開一個系列 我們專講,慢慢來。
![](https://git.imweb.io/hdong/ImageBed/raw/master/EasyLoggerImages/xiangmujiegou.png)
這裡可以看到一個非常乾淨的專案就創建出來了,專案結構就是這樣,裡面的詳情可以去看老張的第二個章節,講的很明白,我就不在重複了
https://www.cnblogs.com/laozhang-is-phi/p/9495620.html
進階可以去看開源原始碼 https://github.com/aspnet/MetaPackages/tree/master/src/Microsoft.AspNetCore
然後我們直接F5 啟動專案看看 是不是新建的專案是不是沒有問題,專案一切正常,我們開始進入正軌。
#專案思考
##1.分表
分表這個功能,就是把相同結構不同名稱的多張表資料讀取出來,這個部分其實很簡單,我們常見的ORM都支援切換表名進行查詢。
##2.分庫
分庫如何實現呢。
先來看一下我們常用的ORM框架是如何連線資料庫的。
![](https://git.imweb.io/hdong/ImageBed/raw/master/EasyLoggerImages/SqlSugarCLien.png) SqlSugar連線資料庫
![](https://git.imweb.io/hdong/ImageBed/raw/master/EasyLoggerImages/FreeSqlClient.png) FreeSql連線資料庫
它們有一個共同點,NEW一個物件傳遞資料庫連線字串,設定好資料庫型別,各自的xxx配置,就會得到一個連線的Client,然後就可以進行CRUD了。
那New多個物件,每個物件都是不同的連線字串豈不是就可以操作多個數據庫了。
![](https://git.imweb.io/hdong/ImageBed/raw/master/EasyLoggerImages/errsql.PNG)
大家寫程式碼可別直接在業務/資料訪問層這麼寫,整不好就讓你下班領盒飯了。
那麼這種情境下因該如何設計能讓程式碼更加規範、易擴充套件呢!
##3.思考
思路就是這樣,那麼如果讓你來設計,你會怎麼做呢,大家可以自己嘗試著先去根據自己想法設計看看,然後我們下一節,我來給講解我的設計方案。
#設計要求
為了提高難度設計難度我們來同時相容FreeSql、SqlSugar2款現在最熱門的ORM。
我們設計要做到:
####易擴充套件(就算再來一個我也能輕鬆支援)
####切換快(不改動業務程式碼前提下,2個ORM框架我想用誰就用誰,隨便切換)
####可共存(可以取2款ORM各自優點在一起開發使用)
#實戰
##1.制定規範
####①、我們先新建一個類庫 EasyLogger.DbStorage(ps:該類庫用於制定規範,提供介面)
####②、新建 Interface 資料夾
####③、新建泛型介面 IAnyStorage (儲存器)
該介面規範字典操作標準方法。
這裡使用的ConcurrentDictionary 是一個併發字典。
![](https://git.imweb.io/hdong/ImageBed/raw/master/EasyLoggerImages/IAngStorage.PNG)
```
public interface IAnyStorage
where T : class
{
ConcurrentDictionary DataMap { get; }
T GetByName(string name, string defaultName);
void AddOrUpdate(string name, T val);
void Remove(string name);
void Clear();
}
```
##2.遵循規範
我們先做SqlSugar的版本
###①、新建 EasyLogger.SqlSugarDbStorage類庫
###②、新建 Interface 資料夾
###③、新建 ISqlSugarProviderStorage (SqlSugar連線提供程式儲存器) 介面繼承IAnyStorage
>繼承IAnyStorage因為他是泛型繼承它,我們需要傳遞一個引數,他是什麼呢?當然是我們的SqlSugar連線提供程式了。
我們安裝NuGet包安裝 sqlSugarCore
###⑤、新建ISqlSugarProvider (SqlSugar連線提供程式) 介面作為泛型引數傳入
![](https://git.imweb.io/hdong/ImageBed/raw/master/EasyLoggerImages/13151911.png)
![](https://git.imweb.io/hdong/ImageBed/raw/master/EasyLoggerImages/3151931.png)
###⑥、新建Impl 資料夾
###⑦、新建 DefaultSqlSugarProviderStorage類繼承 ISqlSugarProviderStorage 進行SqlSugar連線提供程式儲存器的具體實現
![](https://git.imweb.io/hdong/ImageBed/raw/master/EasyLoggerImages/813152539.png)
```
public class DefaultSqlSugarProviderStorage : ISqlSugarProviderStorage
{
public ConcurrentDictionary DataMap { get; private set; }
public DefaultSqlSugarProviderStorage(IServiceProvider serviceProvider)
{
DataMap = new ConcurrentDictionary();
var tmpDataMap = serviceProvider.GetServices()
.ToDictionary(item => item.ProviderName);
foreach (var item in tmpDataMap)
{
this.AddOrUpdate(item.Key, item.Value);
}
}
public void AddOrUpdate(string name, ISqlSugarProvider val)
{
DataMap[name] = val;
}
public void Clear()
{
DataMap.Clear();
}
public ISqlSugarProvider GetByName(string name, string defaultName)
{
ISqlSugarProvider result = null;
if (name == null)
{
if (!DataMap.TryGetValue(defaultName, out result))
{
throw new Exception("沒有找到 DefaultName Provider");
}
return result;
}
else if (DataMap.TryGetValue(name, out result))
{
return result;
}
throw new ArgumentException($"沒有找到 {name} Provider");
}
public void Remove(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
return;
}
this.DataMap.TryRemove(name, out ISqlSugarProvider result);
}
}
```
字典的操作我想都能看明白,基礎差的朋友可能會覺得不懂的就是下面這句,這是做什麼呢,其實就是我們把注入ISqlSugarProvider的實現都查詢
出來,放到我們的DateMap集合中(好處後面我會實踐給大家講到),到此我們SqlSugar連線提供程式儲存庫就完成了。
```
var tmpDataMap = serviceProvider.GetServices()
.ToDictionary(item => item.ProviderName);
foreach (var item in tmpDataMap)
{
this.AddOrUpdate(item.Key, item.Value);
}
```
差點SqlSugar連線提供程式的實現給漏了。
我們在 Impl 資料夾 新建 SqlSugarProvider類,繼承ISqlSugarProvider介面。
![](https://git.imweb.io/hdong/ImageBed/raw/master/EasyLoggerImages/13161107.png)
```
public class SqlSugarProvider : ISqlSugarProvider
{
public string ProviderName { get; set; }
public SqlSugarClient Sugar { get; set; }
public SqlSugarProvider()
{
this.Sugar = this.CreateSqlSugar();
this.ProviderName = "DefaultSqlSugar";
}
private SqlSugarClient CreateSqlSugar()
{
// todo 臨時
var db = new SqlSugarClient(
new ConnectionConfig()
{
ConnectionString = "server=.;uid=sa;pwd=@jhl85661501;database=SqlSugar4XTest",
DbType = DbType.SqlServer,//設定資料庫型別
IsAutoCloseConnection = true,//自動釋放資料務,如果存在事務,在事務結束後釋放
InitKeyType = InitKeyType.Attribute //從實體特性中讀取主鍵自增列資訊
});
return db;
}
public void Dispose()
{
this.Sugar.Dispose();
}
}
```
CreateSqlSugar 方法是我從SqlSugar官方複製過來的,大家注意把ConnectionString改成自己的資料庫連線。
我們現在來配置一下 Startup 先看看效果。
![](https://git.imweb.io/hdong/ImageBed/raw/master/EasyLoggerImages/3162050.png)
```
app.Use(async (context, next) =>
{
var sqlStorage = app.ApplicationServices.GetService();
var sugarClient = sqlStorage.GetByName(null, "DefaultSqlSugar").Sugar;
Console.WriteLine("檢視sugarClient");
});
```
我們成功的從SqlSugar連線提供程式儲存中,拿到SqlSugarClient連線。
#補充
下面程式碼是我在技術群裡看到使用FreeSql的方式,這麼寫沒有問題簡單單例的實現,我推薦大家使用上文中依賴注入的方式來使用!
![](https://git.imweb.io/hdong/ImageBed/raw/master/EasyLoggerImages/o_20081405020500814125827.png)
![](https://git.imweb.io/hdong/ImageBed/raw/master/EasyLoggerImages/o_20081405021100814125834.png)
#結尾
回顧一下本節的內容。
#####1.我們新建統一的泛型 IAnyStorage介面(連線提供程式儲存庫)來規範呼叫。
#####2.我們建立了 ISqlSugarProviderStorage (SqlSugar連線提供程式儲存庫) 通過繼承IAnyStorage來規範介面實現。
#####3.新建了 ISqlSugarProvider (SqlSugar連線提供程式).
#####4.我們完善了介面的實現。
#####5.我們成功在 Startup 中新建一個簡單的中介軟體 成功從SqlSugar連線提供程式儲存中,拿到SqlSugarClient連線。
#思考問題
我們把資料庫連線字串寫在(SqlSugar連線提供程式)合理嗎,是不是依賴太深,這個時候我們就用到控制反轉來降低依賴性,那麼具體怎麼做呢!