配置資料庫表首碼 "ABP踩坑記錄 目錄" 本篇其實和ABP關係並不大,主要是EF Core的一些應用 . 。 起因 支持資料庫表首碼應該是很多應用中比較常見的功能,而在ABP中並沒直接提供這一功能,所以在我們的應用中,我們轉而藉助EF Core的配置來實現資料庫表首碼的配置。 解決方案 這裡我結合 ...
配置資料庫表首碼
本篇其實和ABP關係並不大,主要是EF Core的一些應用-.-。
起因
支持資料庫表首碼應該是很多應用中比較常見的功能,而在ABP中並沒直接提供這一功能,所以在我們的應用中,我們轉而藉助EF Core的配置來實現資料庫表首碼的配置。
解決方案
這裡我結合了Fluent API和數據註解的形式進行配置。
首先,約定所有自定義的表,在其實體類型上都標註了[Table("tablename")]
屬性。
然後在QincaiDbContext
中重載OnModelCreating
方法。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var entityTypes = modelBuilder.Model.GetEntityTypes().ToList();
// 設置自定義表首碼
foreach (var entityType in entityTypes)
{
if (entityType.ClrType
.GetCustomAttributes(typeof(TableAttribute), true)
.FirstOrDefault() is TableAttribute table)
{
// 如果你的表名就是實體類型名的話,可以修改為如下形式,就不必給出[table]的Name參數
// string tableName = tablePrefix + entityType.ClrType.Name;
// 如若有其他需求也可在此進行修改
string tableName = tablePrefix + table.Name;
modelBuilder.Entity(entityType.ClrType)
.ToTable(tableName);
}
}
// 設置內置表首碼
modelBuilder.ChangeAbpTablePrefix<Tenant, Role, User>(tablePrefix);
}
經歷
因為採用的Module Zero模板,其資料庫上下文中已包含部分內置表,ABP Module Zero也提供了拓展方法ModelBuilder.ChangeAbpTablePrefix<TTenant, TRole, TUser>
以便於修改表首碼,其預設的表首碼為Abp
。
剩下就是配置我們自己定義的表的首碼,這裡我們考慮將表首碼以字元串常量的形式儲存。
參考資料:EF Core文檔
如果你喜歡用Fluent API來配置資料庫,那麼解決方案就很簡單了,直接拼接字元串生成新表名即可。
可以參考以下代碼:
// 這裡tablePrefix為字元串常量
modelBuilder.Entity<Blog>().ToTable(tablePrefix + "Blogs");
但是,如果每一個類型都需要配置的話,不免顯得有些冗長。因此,我便考慮通過反射的方式來統一配置表首碼。
首先,我們需要獲取到所有的實體類型。
// 這裡需要註意需要將迭代對象作臨時存儲
// 直接foreach的話會報錯,即不能修改迭代中的對象
var entityTypes = modelBuilder.Model.GetEntityTypes().ToList();
foreach (var entityType in entityTypes)
{
// 配置表首碼
// ...
}
然後便是表首碼的配置,一開始我的想法是直接通過實體類型名來配置表名,但在考慮實際情況後,我認為還是需要有一定的靈活度使得該解決方案更通用。
最終,這裡我選擇用數據註解的方式來為指定表名,即利用[Table]
特性。
因為,這是EF Core官方所提供配置方式之一,不會讓大家因為配置表首碼而增刪過多代碼。
我們會用到[Table]
特性的Name參數,例如這樣:
using System.ComponentModel.DataAnnotations.Schema;
[Table("blogs")]
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}
因為特性並不支持在其參數中引用字元串常量,所以
[Table($"{tablePrefix}blogs")]
這種寫法是不存在的,大家清醒一下。
然後,就是使用Fluent API來配置表首碼了。
// 通過反射獲取該實體類上定義的TableAttribute
// 這裡沒有處理table為null的情況
TableAttribute table = (TableAttribute)entityType.ClrType
.GetCustomAttributes(typeof(TableAttribute), true)
.FirstOrDefault();
// 利用TableAttribute中的Name參數來配置表名
modelBuilder.Entity(entityType.ClrType)
.ToTable(tablePrefix + table.Name);
結合模式匹配語法,我們可以寫出如下形式:
// 若table為null,模式匹配結果將為false
if (entityType.ClrType
.GetCustomAttributes(typeof(TableAttribute), true)
.FirstOrDefault() is TableAttribute table)
{
modelBuilder.Entity(entityType.ClrType)
.ToTable(tablePrefix + table.Name);
}
最後,還沒有完!!
還記得在一開始,我就說了ABP Module Zero內置表有預設的首碼Abp
,也就是說在上述反射獲得的內置表table.Name
將是Abpxxx
的形式,如果你不希望表名中出現Abp,可以在反射之後再使用Abp提供的拓展方法將表名修改一下。