002从零开始入门Entity Framework Core——DbContext生存期、配置和初始化
阅读须知:本文为入门介绍、指引文章,所示代码皆为最简易(或仅为实现功能)的演示示例版本,不一定切实符合个人(企业)实际开发需求。
一、DbContext生存期
DbContext 的生存期从创建实例时开始,并在释放实例时结束。 DbContext 实例旨在用于单个工作单元。这意味着 DbContext 实例的生存期通常很短。
使用 Entity Framework Core (EF Core) 时的典型工作单元包括:
- 创建 DbContext 实例
- 根据上下文跟踪实体实例。 实体将在以下情况下被跟踪
- 正在从查询返回
- 正在添加或附加到上下文
- 根据需要对所跟踪的实体进行更改以实现业务规则
- 调用 SaveChanges 或 SaveChangesAsync。 EF Core 检测所做的更改,并将这些更改写入数据库
- 释放 DbContext 实例
注意事项:
1、使用后释放 DbContext 非常重要。这可确保释放所有非托管资源,并注销任何事件或其他挂钩,以防止在实例保持引用时出现内存泄漏。
2、DbContext 不是线程安全的。不要在线程之间共享上下文。请确保在继续使用上下文实例之前,等待所有异步调用。
3、EF Core 代码引发的 InvalidOperationException 可以使上下文进入不可恢复的状态。此类异常指示程序错误,并且不旨在从其中恢复。
二、ASP.NET Core 依赖关系注入中的 DbContext
在许多 Web 应用程序中,每个 HTTP 请求都对应于单个工作单元。这使得上下文生存期与请求的生存期相关,成为 Web 应用程序的一个良好默认值。
使用依赖关系注入配置 ASP.NET Core 应用程序。可以在 Startup.cs 文件的 ConfigureServices 方法中,用 AddDbContext 扩展方法将 EF Core 添加到此处进行配置。
本文中我使用的是 MySQL 数据库,例如:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
#region 配置MySQL数据库
var connectionString = "server=数据库部署的服务器地址;user=数据库登录用户名;password=数据库登录密码;database=数据库名;charset=utf8";
var serverVersion = new MySqlServerVersion(new Version(5, 7, 22));
services.AddDbContext<CustomAppDbContext>(
dbContextOptions => dbContextOptions
.UseMySql(connectionString, serverVersion)
);
#endregion
}
此示例将名为 CustomAppDbContext 的 DbContext 子类注册为 ASP.NET Core 应用程序服务提供程序(也称为依赖关系注入容器)中的作用域服务。上下文配置为使用 MySQL 数据库提供程序,并将从 ASP.NET Core 配置读取连接字符串。在 ConfigureServices 中的何处调用 AddDbContext 通常不重要。
F12转到 DbContext 类的定义,发现其有参构造函数定义形式为:
public DbContext([NotNullAttribute] DbContextOptions options);
CustomAppDbContext 类必须公开具有 DbContextOptions<CustomAppDbContext> 参数的公共构造函数。这是将 AddDbContext 的上下文配置传递到 DbContext 的方式。例如:
public class CustomAppDbContext : DbContext
{
public CustomAppDbContext(DbContextOptions<CustomAppDbContext> options) : base(options)//调用父类的构造函数
{
}
public DbSet<Student> Student { get; set; }
}
其中 Student 类如下所示:
public partial class Student
{
public string Id { get; set; }
public string Name { get; set; }
public DateTime? JoinTime { get; set; }
public int Sex { get; set; }
}
然后,CustomAppDbContext 可以通过构造函数注入在 ASP.NET Core 控制器或其他服务中使用。例如:
public class MyController : Controller
{
private readonly CustomAppDbContext _context;
public MyController(CustomAppDbContext context)//构造函数
{
_context = context;
}
public JsonResult Index()
{
_context.Student.Add(new Student
{
Id = "10001",
Name = "张三",
JoinTime = DateTime.Now,
Sex = 1
});
_context.SaveChanges();
return new JsonResult("success");
}
}
最终结果是为每个请求创建一个 CustomAppDbContext 实例,并传递给控制器,以在请求结束后释放前执行工作单元。
三、使用“new”的简单的 DbContext 初始化
可以按照常规的 .NET 方式构造 DbContext 实例,例如,使用 C# 中的 new。可以通过重写 OnConfiguring 方法或通过将选项传递给构造函数来执行配置。
1、重写 OnConfiguring 方法。
F12转到 DbContext 类的定义,发现 OnConfiguring 方法的定义形式为:
protected internal virtual void OnConfiguring(DbContextOptionsBuilder optionsBuilder);
DbContext 子类的代码示例如下所示:
public class NewCustomAppDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseMySql("server=数据库部署的服务器地址;user=数据库登录用户名;password=数据库登录密码;database=数据库名;charset=utf8", new MySqlServerVersion(new Version(5, 7, 22)));
}
public DbSet<Student> Student { get; set; }
}
此种方式构造的 DbContext 实例在控制器方法中调用如下所示:
public class MyNewController : Controller
{
public string Index()
{
using var db = new NewCustomAppDbContext();
var list = db.Student.ToList();
return JsonConvert.SerializeObject(list);
}
}
2、通过 DbContext 构造函数传递配置
通过此模式,我们还可以轻松地通过 DbContext 构造函数传递配置(如连接字符串)。例如:
public class NewCustomAppDbContext : DbContext
{
private readonly string _connectionString;
public NewCustomAppDbContext(string connectionString)
{
_connectionString = connectionString;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseMySql(_connectionString, new MySqlServerVersion(new Version(5, 7, 22)));
}
public DbSet<Student> Student { get; set; }
}
此种方式构造的 DbContext 实例在控制器方法中调用如下所示:
public class MyNewController : Controller
{
public string Index()
{
using var db = new NewCustomAppDbContext("server=数据库部署的服务器地址;user=数据库登录用户名;password=数据库登录密码;database=数据库名;charset=utf8");
var list = db.Student.ToList();
return JsonConvert.SerializeObject(list);
}
}
3、使用 DbContextOptionsBuilder 创建 DbContextOptions 对象
可以使用 DbContextOptionsBuilder 创建 DbContextOptions 对象,然后将该对象传递到 DbContext 构造函数。这使得为依赖关系注入配置的 DbContext 也能显式构造。例如:
public class DICustomAppDbContext:DbContext
{
public DICustomAppDbContext(DbContextOptions<DICustomAppDbContext> optionsBuilder):base(optionsBuilder)
{
}
public DbSet<Student> Student { get; set; }
}
此种构造方式,在 Controller 中可以创建 DbContextOptions,并可以显式调用构造函数,代码如下所示:
public class MyDIController : Controller
{
private readonly string _connectionString = "server=数据库部署的服务器地址;user=数据库登录用户名;password=数据库登录密码;database=数据库名;charset=utf8";
private readonly MySqlServerVersion _serverVersion = new MySqlServerVersion(new Version(5, 7, 22));
public string Index()
{
var contextOptions = new DbContextOptionsBuilder<DICustomAppDbContext>()
.UseMySql(_connectionString, _serverVersion)
.Options;
using var context = new DICustomAppDbContext(contextOptions);
var list = context.Student.ToList();
return JsonConvert.SerializeObject(list);
}
}
四、使用 DbContext 工厂
某些应用程序类型(例如 ASP.NET Core Blazor)使用依赖关系注入,但不创建与所需的 DbContext 生存期一致的服务作用域。即使存在这样的对齐方式,应用程序也可能需要在此作用域内执行多个工作单元。例如,单个 HTTP 请求中的多个工作单元。
在这些情况下,可以使用 AddDbContextFactory 来注册工厂以创建 DbContext 实例。例如:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
#region 配置MySQL数据库
var connectionString = "server=数据库部署的服务器地址;user=数据库登录用户名;password=数据库登录密码;database=数据库名;charset=utf8";
var serverVersion = new MySqlServerVersion(new Version(5, 7, 22));
services.AddDbContextFactory<FactoryCustomAppDbContext>(
dbContextOptions => dbContextOptions
.UseMySql(connectionString, serverVersion)
);
#endregion
}
FactoryCustomAppDbContext 类必须公开具有 DbContextOptions<FactoryCustomAppDbContext> 参数的公共构造函数。此模式与上面传统 ASP.NET Core 部分中使用的模式相同。例如:
public class FactoryCustomAppDbContext : DbContext
{
public FactoryCustomAppDbContext(DbContextOptions<FactoryCustomAppDbContext> options) : base(options)
{
}
public DbSet<Student> Student { get; set; }
}
然后,可以通过构造函数注入在其他服务中使用 DbContextFactory 工厂。最后,可以使用注入的工厂在服务代码中构造 DbContext 实例。例如:
public class MyFactoryController : Controller
{
private readonly IDbContextFactory<FactoryCustomAppDbContext> _contextFactory;
public MyFactoryController(IDbContextFactory<FactoryCustomAppDbContext> contextFactory)
{
_contextFactory = contextFactory;
}
public string Index()
{
using (var context = _contextFactory.CreateDbContext())
{
var list = context.Student.ToList();
return JsonConvert.SerializeObject(list);
}
}
}
请注意,以这种方式创建的 DbContext 实例并非由应用程序的服务提供程序进行管理,因此必须由应用程序释放。
五、DbContextOptions
所有 DbContext 配置的起始点都是 DbContextOptionsBuilder。可以通过以下三种方式获取此生成器:
- 在 AddDbContext 和相关方法中
- 在 OnConfiguring 中
- 使用 new 显式构造
每种配置方式的示例在本文上述内容中都进行了讲解和代码展示。无论生成器来自何处,都可以应用相同的配置。此外,无论如何构造上下文,都将始终调用 OnConfiguring。这意味着即使使用 AddDbContext,OnConfiguring 也可用于执行其他配置。
六、配置数据库提供程序
每个 DbContext 实例都必须配置为使用一个且仅一个数据库提供程序。(DbContext 子类型的不同实例可用于不同的数据库提供程序,但一个实例只能使用一个。)一个数据库提供程序要使用一个特定的 Use* 调用进行配置。
例如,若要使用 MySQL 数据库提供程序:
public class MySQLAppDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseMySql("数据库连接字符串", new MySqlServerVersion(new Version(5, 7, 22)));
}
}
例如,若要使用 SQL Server 数据库提供程序:
public class SQLServerApplicationDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("数据库连接字符串");
}
}
这些 Use* 方法是由数据库提供程序实现的扩展方法。 这意味着必须先安装数据库提供程序 NuGet 包,然后才能使用扩展方法。
EF Core 数据库提供程序广泛使用扩展方法。如果编译器指示找不到方法,请确保已安装提供程序的 NuGet 包,并且在代码中已有 using Microsoft.EntityFrameworkCore;。
下表包含常见数据库提供程序的示例。
| 数据库系统 | 配置示例 | NuGet 程序包 |
|---|---|---|
| SQL Server 或 Azure SQL | .UseSqlServer(connectionString) | Microsoft.EntityFrameworkCore.SqlServer |
| Azure Cosmos DB | .UseCosmos(connectionString, databaseName) | Microsoft.EntityFrameworkCore.Cosmos |
| SQLite | .UseSqlite(connectionString) | Microsoft.EntityFrameworkCore.Sqlite |
| EF Core 内存中数据库 | .UseInMemoryDatabase(databaseName) | Microsoft.EntityFrameworkCore.InMemory |
| PostgreSQL | .UseNpgsql(connectionString) | Npgsql.EntityFrameworkCore.PostgreSQL |
| MySQL/MariaDB | .UseMySql(connectionString) | Pomelo.EntityFrameworkCore.MySql |
| Oracle | .UseOracle(connectionString) | Oracle.EntityFrameworkCore |
六、避免 DbContext 线程处理问题
Entity Framework Core 不支持在同一 DbContext 实例上运行多个并行操作。这包括异步查询的并行执行以及从多个线程进行的任何显式并发使用。因此,始终立即 await 异步调用,或对并行执行的操作使用单独的 DbContext 实例。
当 EF Core 检测到尝试同时使用 DbContext 实例的情况时,你将看到 InvalidOperationException,其中包含类似于以下内容的消息:
A second operation started on this context before a previous operation completed. This is usually caused by different threads using the same instance of DbContext, however instance members are not guaranteed to be thread safe.
翻译成中文就是:
在上一个操作完成之前,在此上下文上启动了第二个操作。这通常是由不同线程使用相同的DbContext实例引起的,但不保证实例成员是线程安全的。
当并发访问未被检测到时,可能会导致未定义的行为、应用程序崩溃和数据损坏。
七、异步操作缺陷
使用异步方法,EF Core 可以启动以非阻挡式访问数据库的操作。但是,如果调用方不等待其中一个方法完成,而是继续对 DbContext 执行其他操作,则 DbContext 的状态可能会(并且很可能会)损坏。
应始终立即等待 EF Core 异步方法。
-------------------------------本篇文章到此结束-------------------------------------
002从零开始入门Entity Framework Core——DbContext生存期、配置和初始化的更多相关文章
- 001从零开始入门Entity Framework Core——基础知识
Entity Framework (EF) Core 是轻量化.可扩展.开源和跨平台版的常用 Entity Framework 数据访问技术. 一.什么是 Entity Framework Core ...
- .Net Entity Framework Core 用 HasColumnType 配置浮点数精度
一.前言 前段时间用.Net Entity Framework core搭建框架,需要配置浮点数的精度,发现.Net Entity Framework core 并没有HasPrecision方法.在 ...
- EF Entity Framework Core DBContext中文文档
Add(Object) 以添加状态开始跟踪给定的实体和任何其他尚未被跟踪的可访问实体,以便在调用SaveChanges()时将它们插入数据库.使用State设置单个实体的状态. Add<TEnt ...
- ASP.NET Core 配置 Entity Framework Core - ASP.NET Core 基础教程 - 简单教程,简单编程
原文:ASP.NET Core 配置 Entity Framework Core - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 配置 Entity Fram ...
- Entity Framework Core(EF Core) 最简单的入门示例
目录 概述 基于 .NET Core 的 EF Core 入门 创建新项目 更改当前目录 安装 Entity Framework Core 创建模型 创建数据库 使用模型 基于 ASP.NET Cor ...
- Entity Framework Core 2.0 使用入门
一.前言 Entity Framework(后面简称EF)作为微软家的ORM,自然而然从.NET Framework延续到了.NET Core.以前我也嫌弃EF太重而不去使用它,但是EF Core(E ...
- Entity Framework Core 2.0 入门简介
不多说废话了, 直接切入正题. EF Core支持情况 EF Core的数据库Providers: 此外还即将支持CosmosDB和 Oracle. EFCore 2.0新的东西: 查询: EF.Fu ...
- Entity Framework Core 2.0 入门
该文章比较基础, 不多说废话了, 直接切入正题. 该文分以下几点: 创建Model和数据库 使用Model与数据库交互 查询和保存关联数据 EF Core支持情况 EF Core的数据库Provide ...
- ASP.NET Core 入门教程 8、ASP.NET Core + Entity Framework Core 数据访问入门
一.前言 1.本教程主要内容 ASP.NET Core MVC 集成 EF Core 介绍&操作步骤 ASP.NET Core MVC 使用 EF Core + Linq to Entity ...
随机推荐
- 【前端面试】(二)JavaScript加法运算
视频链接:JavaScript加法运算 - Web前端工程师面试题讲解 数值 + 数值 首先看菜鸟教程有关于数值对象的教程 JavaScript Number 对象 可以知道Infinity , -I ...
- day01 Java_JVM,JCR,JDK
精华笔记: java开发环境: 编译运行过程: 编译期:.java源文件,经过编译,生成.class字节码文件 运行期:JVM加载.class并运行.class(0和1) 特点:跨平台.一次编程到处使 ...
- C++指针和结构体基础知识
学习C++首先要回忆起C语言当中的指针和结构体知识,本文作者将通过一段代码来总结指针和结构体基础知识:指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址.就像其他变量或常量一样,您必须在使 ...
- 科学计算库Numpy基础&提升(理解+重要函数讲解)
Intro 对于同样的数值计算任务,使用numpy比直接编写python代码实现 优点: 代码更简洁: numpy直接以数组.矩阵为粒度计算并且支持大量的数学函数,而python需要用for循环从底层 ...
- 基于ABP实现DDD--领域服务、应用服务和DTO实践
什么是领域服务呢?领域服务就是领域对象本身的服务,通常是通过多个聚合以实现单个聚合无法处理的逻辑. 一.领域服务实践 接下来将聚合根Issue中的AssignToAsync()方法[将问题分配给用 ...
- dijkstra最短路算法(堆优化)
这个算法不能处理负边情况,有负边,请转到Floyd算法或SPFA算法(SPFA不能处理负环,但能判断负环) SPFA(SLF优化):https://www.cnblogs.com/yifan0305/ ...
- 洛谷 P2073 送花 treap 无指针
看了那么多题解都没做对,结果今早上按自己的思路和模板做了做,然后过了. 平衡树裸题 直接上代码: #include<bits/stdc++.h> #define rint register ...
- 聊聊如何用 Redis 实现分布式锁?
作者:小林coding 计算机八股文网站:https://xiaolincoding.com 哈喽,我是小林. 今天跟大家聊聊两个问题: 如何用 Redis 实现分布式锁? Redis 是如何解决集群 ...
- 序列化和返序列化的概述和对象的序列化流ObjectOutputStream
序列化和返序列化的概述 对象的序列化流ObjectOutputStream Person类: package com.yang.Test.ObjectStreamStudy; import java. ...
- 函数,递归以及dom简单操作
函数 函数概述 函数就是具备某个功能的一个工具.是完成某个功能的一段代码. 系统提供了很多函数,但是并不能包含所有的功能,所以有些功能需要我们自己来写----自定义函数.函数定义好以后,就可以像系统函 ...