DbContext生存期
DbContext的生存期从创建实例开始,并在释放实例时结束。DbContext实例旨在用于单个工作单元。这意味着DbContext实例的生存期通常很短。
工作单元:维护受业务交易影响的对象的列表,并协调更改的注销和并发问题的解决。
在将数据移入和移出数据库时,重要的是要跟踪所做的更改。否则,该数据将不会被写回到数据库中。同样,您必须插入创建的新对象并删除所有删除的对象。
您可以随着对象模型的每次更改来更改数据库,但是这可能导致许多非常小的数据库调用,这最终会非常缓慢。此外,它要求您为整个交互打开一个事务,如果您的业务事务跨越多个请求,则这是不切实际的。如果您需要跟踪已读取的对象,从而避免不一致的读取,则情况甚至更糟。
工作单元会跟踪您在业务交易过程中可能影响数据库的所有操作。完成后,它会计算出由于工作而需要更改数据库的所有工作。英文原文请参考:EAA的P。
EF Core的典型工作单元包括(重点理解这段,有助于我们对EF Core工作原理的理解):
- 创建DbContext实例
- 根据上下文跟踪实体实例。实体将在以下情况下被追踪
- 正在从查询返回
- 正在添加或附加到上下文
- 根据需要对所跟踪的实体进行更改以实现业务规则
- 调用SaveChanges或SaveChangesAsync。EF Core检测所做的更改,并将这些更改写入到数据库
- 释放DbContext
重要知识点:
- 使用后释放DbContext非常重要。这可确保释放所有非托管资源,并注销任何事件或其他挂钩,以防止在实例保持引用时出现内存泄漏。
- DbContext不是线程安全的。不要在线程间共享上下文。请确保在继续使用上下文实例之前,等待所有异步调用。
- EF Core引发的InvalidOperationException可以使上下文进入不可恢复状态。
ASP.NET Core 依赖关系注入中的 DbContext
这使得上下文生存期与请求的生存期相关。
例如:
public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddDbContext<ApplicationDbContext>( options => options.UseSqlServer("name=ConnectionStrings:DefaultConnection")); }
依赖关系注入容器)中的作用域服务。
options => options.UseSqlServer上下文配置为使用 SQL Server 数据库提供程序,
"name=ConnectionStrings:DefaultConnection"表示在 ConfigureServices 中的何处调用 AddDbContext 通常不重要。
例如:
public class ApplicationDbContext : DbContext { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } }
例如:
1 public class MyController 2 { 3 private readonly ApplicationDbContext _context; 4 5 public MyController(ApplicationDbContext context) 6 { 7 _context = context; 8 } 9 }
最终结果是为每个请求创建一个 ApplicationDbContext 实例,并传递给控制器,以在请求结束后释放前执行工作单元。
使用“new”的简单的 DbContext 初始化
例如:
1 public class ApplicationDbContext : DbContext 2 { 3 protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 4 { 5 optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test"); 6 } 7 }
例如:
1 public class ApplicationDbContext : DbContext 2 { 3 private readonly string _connectionString; 4 5 public ApplicationDbContext(string connectionString) 6 { 7 _connectionString = connectionString; 8 } 9 10 protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 11 { 12 optionsBuilder.UseSqlServer(_connectionString); 13 } 14 }
例如,使用上述为 ASP.NET Core 的 Web 应用定义的 ApplicationDbContext 时:
1 public class ApplicationDbContext : DbContext 2 { 3 public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) 4 : base(options) 5 { 6 } 7 }
可以创建 DbContextOptions,并可以显式调用构造函数:
var contextOptions = new DbContextOptionsBuilder<ApplicationDbContext>() .UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test") .Options; using var context = new ApplicationDbContext(contextOptions);
使用 DbContext 工厂(例如对于 Blazor)
例如:
1 public void ConfigureServices(IServiceCollection services) 2 { 3 services.AddDbContextFactory<ApplicationDbContext>(options => 4 options.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test")); 5 }
此模式与上面传统 ASP.NET Core 部分中使用的模式相同。
1 public class ApplicationDbContext : DbContext 2 { 3 public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) 4 : base(options) 5 { 6 } 7 }
例如:
1 private readonly IDbContextFactory<ApplicationDbContext> _contextFactory; 2 3 public MyController(IDbContextFactory<ApplicationDbContext> contextFactory) 4 { 5 _contextFactory = contextFactory; 6 }
例如:
1 public void DoSomething() 2 { 3 using (var context = _contextFactory.CreateDbContext()) 4 { 5 // ... 6 } 7 }
请注意,以这种方式创建的 DbContext 实例并非由应用程序的服务提供程序进行管理,因此必须由应用程序释放。
DbContextOptions
可以通过三种方式获取此生成器:
- 在
AddDbContext和相关方法中 - 在
OnConfiguring中 - 使用
new显式构造
这意味着即使使用 AddDbContext,OnConfiguring 也可用于执行其他配置。
配置数据库提供程序
例如,若要使用 SQL Server 数据库提供程序:
1 public class ApplicationDbContext : DbContext 2 { 3 protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 4 { 5 optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test"); 6 } 7 }
这意味着必须先安装数据库提供程序 NuGet 包,然后才能使用扩展方法。
如果编译器指示找不到方法,请确保已安装提供程序的 NuGet 包,并且你在代码中已有 using Microsoft.EntityFrameworkCore;。
下表包含常见数据库提供程序的示例:
|
|
|
|||
| 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 线程处理问题
因此,始终立即 await 异步调用,或对并行执行的操作使用单独的 DbContext 实例。
当 EF Core 检测到尝试同时使用 DbContext 实例的情况时,你将看到 InvalidOperationException