教程
其他教程預覽
分庫分表項目實戰教程
Git地址: https://github.com/MrChuJiu/EasyLogger
前言
項目涉及到了一些設計模式,如果你看的不是很明白,沒有關系堅持下來,寫完之后去思考去品,你就會有一種突撥開雲霧的感覺,所以請不要在半途感覺自己看不懂選擇放棄,如果我哪里寫的詳細,或者需要修正請聯系我,謝謝。
創建項目
1.SDK安裝
我們開發用的vs版本是2019 .Net Core的版本是3.1
下載 SDK 地址 :https://dotnet.microsoft.com/download
2.新建項目
這里選擇Core 版本是3.1 項目類型是API
Docker支持我們不勾選,我會在后續給大家單獨再開一個系列 我們專講,慢慢來。
這里可以看到一個非常干凈的項目就創建出來了,項目結構就是這樣,里面的詳情可以去看老張的第二個章節,講的很明白,我就不在重復了
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框架是如何連接數據庫的。
SqlSugar連接數據庫
FreeSql連接數據庫
它們有一個共同點,NEW一個對象傳遞數據庫連接字符串,設置好數據庫類型,各自的xxx配置,就會得到一個連接的Client,然后就可以進行CRUD了。
那New多個對象,每個對象都是不同的連接字符串豈不是就可以操作多個數據庫了。
大家寫代碼可別直接在業務/數據訪問層這么寫,整不好就讓你下班領盒飯了。
那么這種情境下因該如何設計能讓代碼更加規范、易擴展呢!
3.思考
思路就是這樣,那么如果讓你來設計,你會怎么做呢,大家可以自己嘗試着先去根據自己想法設計看看,然后我們下一節,我來給講解我的設計方案。
設計要求
為了提高難度設計難度我們來同時兼容FreeSql、SqlSugar2款現在最熱門的ORM。
我們設計要做到:
易擴展(就算再來一個我也能輕松支持)
切換快(不改動業務代碼前提下,2個ORM框架我想用誰就用誰,隨便切換)
可共存(可以取2款ORM各自優點在一起開發使用)
實戰
1.制定規范
①、我們先新建一個類庫 EasyLogger.DbStorage(ps:該類庫用於制定規范,提供接口)
②、新建 Interface 文件夾
③、新建泛型接口 IAnyStorage (存儲器)
該接口規范字典操作標准方法。
這里使用的ConcurrentDictionary 是一個並發字典。
public interface IAnyStorage<T>
where T : class
{
ConcurrentDictionary<string, T> 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連接提供程序) 接口作為泛型參數傳入
⑥、新建Impl 文件夾
⑦、新建 DefaultSqlSugarProviderStorage類繼承 ISqlSugarProviderStorage 進行SqlSugar連接提供程序存儲器的具體實現
public class DefaultSqlSugarProviderStorage : ISqlSugarProviderStorage
{
public ConcurrentDictionary<string, ISqlSugarProvider> DataMap { get; private set; }
public DefaultSqlSugarProviderStorage(IServiceProvider serviceProvider)
{
DataMap = new ConcurrentDictionary<string, ISqlSugarProvider>();
var tmpDataMap = serviceProvider.GetServices<ISqlSugarProvider>()
.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<ISqlSugarProvider>()
.ToDictionary(item => item.ProviderName);
foreach (var item in tmpDataMap)
{
this.AddOrUpdate(item.Key, item.Value);
}
差點SqlSugar連接提供程序的實現給漏了。
我們在 Impl 文件夾 新建 SqlSugarProvider類,繼承ISqlSugarProvider接口。
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 先看看效果。
感謝 @Damn 幫我發現漏掉說明的代碼!
public void ConfigureServices(IServiceCollection services)
{
// 注入
services.AddSingleton<ISqlSugarProvider, SqlSugarProvider>();
services.AddSingleton<ISqlSugarProviderStorage, DefaultSqlSugarProviderStorage>();
services.AddControllers();
}
app.Use(async (context, next) =>
{
var sqlStorage = app.ApplicationServices.GetService<ISqlSugarProviderStorage>();
var sugarClient = sqlStorage.GetByName(null, "DefaultSqlSugar").Sugar;
Console.WriteLine("查看sugarClient");
});
我們成功的從SqlSugar連接提供程序存儲中,拿到SqlSugarClient連接。
補充
下面代碼是我在技術群里看到使用FreeSql的方式,這么寫沒有問題簡單單例的實現,我推薦大家使用上文中依賴注入的方式來使用!
結尾
回顧一下本節的內容。
1.我們新建統一的泛型 IAnyStorage接口(連接提供程序存儲庫)來規范調用。
2.我們創建了 ISqlSugarProviderStorage (SqlSugar連接提供程序存儲庫) 通過繼承IAnyStorage來規范接口實現。
3.新建了 ISqlSugarProvider (SqlSugar連接提供程序).
4.我們完善了接口的實現。
5.我們成功在 Startup 中新建一個簡單的中間件 成功從SqlSugar連接提供程序存儲中,拿到SqlSugarClient連接。
思考問題
我們把數據庫連接字符串寫在(SqlSugar連接提供程序)合理嗎,是不是依賴太深,這個時候我們就用到控制反轉來降低依賴性,那么具體怎么做呢!