EFCore+Scheme(数据库架构)实现分库分表支持多种主流数据库解决方案
EFCore+Scheme(数据库架构)实现分库分表支持多种主流数据库解决方案
目录
一、数据库中的Schema含义
Schema是数据库的组织和结构,在不同的数据库中,Schema被称为模式、数据库架构等。Schema中包含了schema对象,可以是表(table)、列(column)、数据类型(data type)、视图(view)、存储过程(stored procedures)、关系(relationships)、主键(primary key)、外键(foreign key)等。
Schema的主要作用是用于组织和管理数据库对象,通过Schema可以更加有效地对数据库对象进行分组管理,提高数据库的性能和运行效率。
Oracle数据库中,Schema是一个数据库用户的命名空间,Schema和用户是一一对应的,一个用户对应一个Schema,反之亦然。
达梦数据库中,Schema称为模式,含义与Oracle大致相同。
SqlServer数据库中默认Schema是dbo
二、EFCore为什么使用Schema?
EFCore使用Schema的主要原因是为了解决数据库中的命名冲突和逻辑隔离问题。
- 命名冲突:在大型数据库中,不同的开发者或团队可能会创建具有相同名称的对象。通过使用Schema,可以将这些对象逻辑地分隔开,避免命名冲突。
- 逻辑隔离:Schema可以用于逻辑上的隔离,例如,可以将不同业务模块的数据存储在不同的Schema中,从而方便管理和维护。
- 权限控制:通过Schema,可以更细粒度地控制不同用户或角色的访问权限,提高数据的安全性。
☛举2个简单例子:
1、如:Oracle数据库的连接字符串,连接服务名 orcl:
Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=orcl)));User Id=user01;Password=123;
2、如:达梦数据库的连接字符串,默认连接DAMENG库:
Server=localhost;Port=5236;Database=DAMENG;Uid=SYSDBA;Pwd=123;
结论:Oracle和达梦数据库的连接字符串都没有连接具体的数据库名称,在这种情况下需要使用Schema来区分不同的数据库,如:CSFrameworkV6_Normal、CSFrameworkV6_System两个数据库。
三、DbContext上下文OnModelCreating方法介绍
OnModelCreating是DbContext类的一个方法,用于配置数据库模型。在这个方法中,你可以使用Fluent API来配置每个实体的映射关系、属性类型和其他设置。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
//重要:动态注册实体类,替换DbSet<>方式
foreach (var T in _databaseConfig.EntityTypes)
{
modelBuilder.Entity(T);
}
}
四、为什么DbContext.OnModelCreating方法只执行一次?
在Entity Framework Core (EFCore)中,DbContext的OnModelCreating方法仅在数据库上下文的生命周期中执行一次。
因为EFCore基于性能优化,在第一次访问数据库时解析模型,并为模型创建一个数据库架构然后缓存起来,当你下次访问数据库时,EFCore会检查缓存的模型是否与当前模型相同,如果相同,它就不会再次调用OnModelCreating方法。
综上所述,不同数据库使用Schema来区分数据库或表(如:Oracle,达梦数据库),并且EFCore的DbContext.OnModelCreating方法只会执行一次,我们在设计分库分表、支持多种主流数据库程序的时候,实例化DbContext会发生实体模型的Schema混乱,导致严重错误。最常见的错误是DbContext找不到表或视图,因为表属于不同的数据库或Schema。
五、解决方案
1、实体模型设置Schema
在Entity Framework Core (EFCore) 中,有两种方式给实体模型设置Schema:
a) 实体类的属性上使用特性 [Table] 来设置 Schema:
//给实体模型指定Schema
[Table("tb_Customer", Schema = "CSFrameworkV6_Normal")]
public class tb_Customer
{
}
b) 如果为所有实体设置默认的 Schema,可以在 OnModelCreating 方法中配置
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// 设置默认数据库架构
modelBuilder.HasDefaultSchema("CSFrameworkV6_Normal");
//或者为特定的实体设置schema
//modelBuilder.Entity<YourEntity>().ToTable("TableName","SchemaName");
}
2、解决 DbContext.OnModelCreating方法只执行一次的问题
出于性能考虑,DbContext中的OnModelCreating在缺省状态下,只在第一次实例化DbContext时执行,执行后的结果被放在缓存中,供以后的实例使用。然而,在有些情况下,DbContext需要根据调用的场景发生变化,需要重新执行OnModelCreating,这种情况下,需要编写自定义的缓存服务替换默认缓存服务,新的缓存服务根据DbContext的变化确定缓存的键值,如果缓存中没有相应的对象就重新执行OnModelCreating,生成相应的对象保存在缓存中。
编写自定义的ModelCacheFactory工厂,实现IModelCacheKeyFactory接口:
/// <summary>
/// 实现IModelCacheKeyFactory接口,解决OnModelCreating只会执行一次的问题
/// </summary>
public class SchemaModelCacheFactory : IModelCacheKeyFactory
{
public object Create(DbContext context, bool designTime)
{
return new SchemaModelCache(context, designTime);
}
}
编写自定义的缓存服务替换默认的缓存服务:
/// <summary>
/// 自定义ModelCacheKey替换默认缓存服务,新的缓存服务根据DbContext的变化确定缓存的键值,
/// 如果缓存中没有相应的对象就重新执行OnModelCreating,生成相应的对象保存在缓存中
/// </summary>
internal class SchemaModelCache : ModelCacheKey
{
readonly string _schema;
public SchemaModelCache(DbContext context, bool designTime) : base(context, designTime)
{
//当前DbContext的Schema
_schema = (context as GenericDbContext)?.DatabaseConfig?.Schema;
}
protected override bool Equals(ModelCacheKey other)
{
//判断schema是否一致
return base.Equals(other) && (other as SchemaModelCache)?._schema == _schema;
}
public override int GetHashCode()
{
var hashCode = base.GetHashCode() * 168;
if (_schema != null)
{
hashCode ^= _schema.GetHashCode(); //生成新的hash编码
}
return hashCode;
}
}
使用自定义ModelCacheFactory工厂方式,实体模型的[Table]特性不需要指定Schema。
六、其他参考资料
1、ModelBuilder.HasDefaultSchema方法详解
modelBuilder.HasDefaultSchema(string? schema) 方法用于设置默认的数据库架构。
在 Entity Framework Core 中,使用 modelBuilder.HasDefaultSchema 方法来指定默认的数据库架构,我们在定义实体模型时如果没有指定Schema,那么 Entity Framework Core 会自动使用你设置的默认架构。
给实体模型指定Schema,参考:
//给实体模型指定Schema
[Table("tb_Customer", Schema = "CSFrameworkV6_Normal")]
public class tb_Customer
{
}
如果您的软件项目使用分库分表,或者采用多种数据库的情况下,实体模型不能指定Schema,参考:
[Table("tb_Customer")]
public class tb_Customer
{
}
反编译查看方法定义:
public static ModelBuilder HasDefaultSchema(this ModelBuilder modelBuilder, string? schema)
{
Microsoft.EntityFrameworkCore.Utilities.Check.NullButNotEmpty(schema, "schema");
modelBuilder.Model.SetDefaultSchema(schema);
return modelBuilder;
}
七、CSFramework.EF数据库框架应用Schema测试多种数据库
1、CSFramework.EF测试
CSFramework.EF数据库框架应用Schema测试多种数据库(.NET8+EFCore)
测试报告:https://www.cscode.net/archive/newdoc/612063008661509.html
2、CSFramework.EF介绍
CSFramework.EF是轻量级数据库底层框架,基于Entity Framework 实体框架强大功能封装而成。支持三种主流数据库,分别是SqlServer、Oracle、MySQL,支持国产数据库 - 达梦数据库,用户可扩展其他数据如 PostgreSQL,MongoDB,SQLLite等。
CSFramework.EF数据库框架提供IDatabase接口,里面定义了一组通用的接口方法,如增、删、改、查:Add<T>, Update<T>,Remove<T>,GetQuaryable<T>,支持LINQ,SQL脚本查询和操作,支持常用事务、BulkInsert批量插入等功能。
软件介绍:https://www.cscode.net/archive/csframework.ef/363596745297925.html