EFCore+Scheme(数据库架构)实现分库分表支持多种主流数据库解决方案|C/S开发框架
作者:csframework|C/S框架网  发布日期:2024/11/14 23:05:10

EFCore+Scheme(数据库架构)实现分库分表支持多种主流数据库解决方案|C/S开发框架

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来配置每个实体的映射关系、属性类型和其他设置。

C# 全选
        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:

C# 全选
    //给实体模型指定Schema
    [Table("tb_Customer", Schema = "CSFrameworkV6_Normal")]    
    public class tb_Customer
    {
    }

b) 如果为所有实体设置默认的 Schema,可以在 OnModelCreating 方法中配置

C# 全选
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接口:

C# 全选
    /// <summary>
    /// 实现IModelCacheKeyFactory接口,解决OnModelCreating只会执行一次的问题
    /// </summary>
    public class SchemaModelCacheFactory : IModelCacheKeyFactory
    {
        public object Create(DbContext context, bool designTime)
        {
            return new SchemaModelCache(context, designTime);
        }
    }

编写自定义的缓存服务替换默认的缓存服务:

C# 全选
    /// <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,参考:

C# 全选
    //给实体模型指定Schema
    [Table("tb_Customer", Schema = "CSFrameworkV6_Normal")]    
    public class tb_Customer
    {
    }

如果您的软件项目使用分库分表,或者采用多种数据库的情况下,实体模型不能指定Schema,参考:

C# 全选
    [Table("tb_Customer")]    
    public class tb_Customer
    {
    }

反编译查看方法定义:

C# 全选
    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测试

EFCore+Scheme(数据库架构)实现分库分表支持多种主流数据库解决方案|C/S开发框架

CSFramework.EF数据库框架应用Schema测试多种数据库(.NET8+EFCore)

测试报告:https://www.cscode.net/archive/newdoc/612063008661509.html

2、CSFramework.EF介绍

EFCore+Scheme(数据库架构)实现分库分表支持多种主流数据库解决方案|C/S开发框架

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

C/S框架网|原创精神.创造价值.打造精品


扫一扫加作者微信
C/S框架网作者微信 C/S框架网|原创作品.质量保障.竭诚为您服务
上一篇 下一篇