9.1 数据库上下文

9.1.1 数据库上下文#

简单来说,数据库上下文是负责和数据库交互的对象,提供程序对数据库存取提供了大量的方法。

Furion 框架中,默认集成了微软亲儿子:EntityFramework Core ,也就是通常数据库上下文指的是 DbContext 类或它的实现类。

9.1.2 AppDbContext#

在我们实际项目开发过程中,使用 EFCore 提供的 DbContext 操作对象操作数据库有些繁琐和复杂,且默认不具备读写分离、多库等操作功能。

所以,Furion 框架提供了 AppDbContext<TDbContext, TDbContextLocator> 数据库上下文,该上下文继承自 DbContext

特别说明

后续章节,皆采用 EFCore 代替 EntityFramework Core

9.1.3 AppDbContextDbContext 区别#

  • AppDbContext 继承自 DbContext,具备 DbContext 所有功能。
  • AppDbContext 支持多数据库操作泛型版本,如:AppDbContext<TDbContext, TDbContextLocator>
  • AppDbContext 自动配置实体信息,无需在 OnModelCreating 中配置
  • AppDbContext 支持内置多租户支持
  • AppDbContext 支持全局模型配置拦截器
  • AppDbContext 支持数据提交更改多个事件
  • AppDbContext 提供更加强大的模型操作能力,如 Sql 操作,读写分离等
  • AppDbContext 能够得到 Furion 框架更多的功能支持

9.1.4 如何定义数据库上下文#

Furion 框架中了,提供了两种 AppDbContext 定义方式:

  • AppDbContext<TDbContext> 操作默认数据库
  • AppDbContext<TDbContext, TDbContextLocator> 操作 N 个数据库

其中 AppDbContext<TDbContext> 默认继承自 AppDbContext<TDbContext, TDbContextLocator>

下面是数据库上下文创建的多个例子:

9.1.4.1 创建默认数据库上下文#

using Furion.DatabaseAccessor;
using Microsoft.EntityFrameworkCore;
namespace Furion.EntityFramework.Core
{
[AppDbContext("连接字符串或appsetting.json 键")]
public class FurDbContext : AppDbContext<FurDbContext> // 继承 AppDbContext<> 类
{
/// <summary>
/// 继承父类构造函数
/// </summary>
/// <param name="options"></param>
public FurDbContext(DbContextOptions<FurDbContext> options) : base(options)
{
}
}
}

9.1.4.2 创建其他数据库上下文#

using Furion.DatabaseAccessor;
using Microsoft.EntityFrameworkCore;
namespace Furion.EntityFramework.Core
{
[AppDbContext("连接字符串或appsetting.json 键")]
public class FurOtherDbContext : AppDbContext<FurOtherDbContext, FurOtherDbContextLocator> // 继承 AppDbContext<> 类
{
/// <summary>
/// 继承父类构造函数
/// </summary>
/// <param name="options"></param>
public FurOtherDbContext(DbContextOptions<FurOtherDbContext> options) : base(options)
{
}
}
}
特别注意

所有数据库上下文都应该在 Furion.EntityFramework.Core 项目中创建。另外如果系统用到了多个数据库,那么从第二个开始必须指定数据库上下文定位器。关于 TDbContextLocator 将在下一章节 《9.2 数据库上下文定位器》阐述。

9.1.5 配置连接字符串#

Furion 框架提供多种数据库连接字符串配置方式:

  • 在注册数据库服务时配置:AddSqlServerPool<TDbContext>("连接字符串") 方式
  • 使用 [AppDbContext("连接字符串/Key")] 特性方式(只在 AppDbContext 实现类有效推荐
  • 通过重写 OnConfiguring(DbContextOptionsBuilder optionsBuilder) 配置

9.1.5.1 在注册数据库服务时配置#

Furion.EntityFramework.Core\FurEntityFrameworkCoreStartup.cs
using Furion.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
namespace Furion.EntityFramework.Core
{
[AppStartup(600)]
public sealed class FurEntityFrameworkCoreStartup : AppStartup
{
public void ConfigureServices(IServiceCollection services)
{
// 配置数据库上下文,支持N个数据库
services.AddDatabaseAccessor(options =>
{
// 配置默认数据库
options.AddDbPool<FurDbContext>(DbProvider.SqlServer, "连接字符串");
// 配置多个数据库,多个数据库必须指定数据库上下文定位器
options.AddDbPool<SqliteDbContext, SqliteDbContextLocaotr>(DbProvider.Sqlite, "连接字符串");
});
}
}
}
新版 MySQL 注意

MySQL 在新版本包中注册有所修改,所以注册方式为:

services.AddDatabaseAccessor(options =>
{
options.AddDbPool<FurDbContext>($"{DbProvider.MySql}@8.0.22");
});

9.1.5.2 [AppDbContext] 方式配置#

using Furion.DatabaseAccessor;
using Microsoft.EntityFrameworkCore;
namespace Furion.EntityFramework.Core
{
[AppDbContext("DbConnectionString")] // 支持 `appsetting.json` 名或 连接字符串
public class FurDbContext : AppDbContext<FurDbContext>
{
/// <summary>
/// 继承父类构造函数
/// </summary>
/// <param name="options"></param>
public FurDbContext(DbContextOptions<FurDbContext> options) : base(options)
{
}
}
}
小提示

Furion 推荐使用此方式配置数据库连接字符串。

[AppDbContext] 内置属性:

  • ConnectionString:数据库连接字符串,或配置文件中的路径(支持自定义配置查找),或 appsetting.jsonConnectionStrings 配置字符串
  • TablePrefix:当前数据库上下文表统一前缀
  • TableSuffix:当前数据库上下文表统一后缀
  • ProviderName:配置数据库提供器类型,传入 DbProvider.Xxx
  • Mode:配置数据库上下文模式,DbContextMode 枚举类型,取值:
    • Cached:缓存模型数据库上下文,默认值
    • Dynamic:动态模型数据库上下文

9.1.5.3 OnConfiguring 方式配置#

using Furion.DatabaseAccessor;
using Microsoft.EntityFrameworkCore;
namespace Furion.EntityFramework.Core
{
public class FurDbContext : AppDbContext<FurDbContext>
{
/// <summary>
/// 继承父类构造函数
/// </summary>
/// <param name="options"></param>
public FurDbContext(DbContextOptions<FurDbContext> options) : base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder.UseSqlServer("数据库连接字符串");
}
}
}
特别注意

这三种方式可以同时使用,但是有优先级:[AppDbContext] -> 在注册数据库服务时配置 -> OnConfiguring(低到高)

也就是 OnConfiguring 配置会覆盖 在注册数据库服务时配置 配置,在注册数据库服务时配置 配置会覆盖 [AppDbContext] 配置所配置的连接字符串。

9.1.6 数据库上下文定义位置#

特别注意

Furion 框架中,数据库上下文需定义在 Furion.EntityFramework.Core 中,且每一个数据库上下文都必须拥有唯一的 DbContextLocator 定位器

9.1.7 数据库上下文注册#

数据库上下文配置好数据库连接字符串后,需要注册该数据库上下文,并指定数据库类型,如:

Furion\framework\Furion.EntityFramework.Core\FurEntityFrameworkCoreStartup.cs
using Furion.DatabaseAccessor;
using Microsoft.Extensions.DependencyInjection;
namespace Furion.EntityFramework.Core
{
[AppStartup(600)]
public sealed class FurEntityFrameworkCoreStartup : AppStartup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddDatabaseAccessor(options =>
{
options.AddDbPool<FurDbContext>(DbProvider.Sqlite);
});
}
}
}

如果有多个数据库操作,那么从第二个起,就需要绑定数据库上下文定位器,如:

options.AddDbPool<FurDbContext>(DbProvider.Sqlite); // 第一个数据库
options.AddDbPool<SecondDbContext, SecondDbContextDbContextLocator>(DbProvider.SqlServer); // 第二个数据库
options.AddDbPool<ThirdDbContext, ThirdDbContextDbContextLocator>(DbProvider.SqlServer); // 第三个数据库

9.1.8 动态数据库上下文对象#

Furion 框架中,数据库上下文是定义在 Furion.EntityFramework.Core 项目层,并且该层不被 Furion.ApplicationFurion.Core 等层引用。

所以就不能直接在 Furion.Application 项目层直接使用 Furion.EntityFramework.Core 定义的数据库上下文。

Furion 为了解决这个问题,提供了两种方式处理:

  • respository.Context :当前数据库上下文对象,返回是 DbContext 抽象类型
  • respository.DynamicContext:当前数据库上下文对象,返回的是 dynamic 类型

如果你只是想使用 DbContext 的功能,直接使用 respository.Context 即可,如:

respository.Context.SaveChanges();

如果你想能够获取具体的数据库上下文类型,如 MyDbContext,那么使用 respository.DynamicContext 就可以获取到具体的 MyDbContext 类型。如:

var persons = respository.DynamicContext.Persons.Find(1);
var users = respository.DynamicContext.Users;

这样就可以直接操作 MyDbContext 定义的属性和方法了。

9.1.9 反馈与建议#

与我们交流

给 Furion 提 Issue


了解更多

想了解更多 数据库上下文 知识可查阅 EF Core - 配置 DbContext 章节。