配置数据库表前缀
本篇其实和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(tablePrefix);}
经历
因为采用的Module Zero模板,其数据库上下文中已包含部分内置表,ABP Module Zero也提供了拓展方法ModelBuilder.ChangeAbpTablePrefix<TTenant, TRole, TUser>
以便于修改表前缀,其默认的表前缀为Abp
。
剩下就是配置我们自己定义的表的前缀,这里我们考虑将表前缀以字符串常量的形式储存。
参考资料:
如果你喜欢用Fluent API来配置数据库,那么解决方案就很简单了,直接拼接字符串生成新表名即可。
可以参考以下代码:
// 这里tablePrefix为字符串常量modelBuilder.Entity().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,模式匹配结果将为falseif (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提供的拓展方法将表名修改一下。