一、什么是DbContext
1.DbContext,数据库上下文,用于构建数据库交互对象
二、创建与使用DbContext的几种方式
1.在服务中直接new一个Context对象,DbContext的配置方式同第十三节环境搭建
2.通过服务注入的方式创建上下文
3.配置DbContextOptionsBuilder(就是DbContext类中的相关代码配置)
public class HomeController: Controller
{
//注入
private readonly ILogger<HomeController> _logger:
private readonly IConfiguration _configuration;
public HomeController(ILogger<HomeController> logger,IConfiguration configuration)
{
_logger=logger:
_configuration = configuration:
}
public IActionResult Index()
{
//创建一个DbContextOptionsBuilder对象
DbContextOptionsBuilder<BloggingContext> optionsBuilder=new DbContextOptionsBuilder<BloggingContext>():
//配置数据连接
optionsBuilder.UseSqlServer(_configuration.GetConnectionString("Blogs")):
BloggingContext context new BloggingContext (optionsBuilder.Options):
context.Blogs.Add(new Blog(){Url ="MyTestURL"}):
context.SaveChanges();
return View();
}
}
- 如果DbContext类不需要再次被继承,则使用带泛型的Options,否则使用不带泛型的Options
三、DbContext的生命周期
1.DbContext 的生存期从创建实例时开始,并在释放实例时结束。 DbContext 实例旨在用于单个工作单元。 这意味着 DbContext 实例的生存期通常很短。(来自于官方文档)
(1)什么是“工作单元”:在业务逻辑的代码中,从初始化一个DbContext对象,并使用该对象完成一系列对于数据库的操作,最后提交操作,销毁DbContext实例,这一过程称为一个工作单元。
(2)工作单元将持续跟踪在可能影响数据库的业务事务中执行的所有操作。 当完成操作后,它将找出更改数据库作为工作结果时需要执行的所有操作。
(3)一个典型的工作单元:new DbContext()=>context.SaveChanges()=>context.Dispose()
四、DbContext工厂的使用
1.由于DbContext默认的生命周期是Scope。当一个工作单元结束时,DbContext对象会被立即释放。假设一个方法体中存在多个工作单元时,就需要使用“工厂”来管理DbContext。(官方文档中指出“某些应用程序类型(例如 ASP.NET Core Blazor)使用依赖关系注入,但不创建与所需的 DbContext 生存期一致的服务作用域”)
2.工厂的使用
(1)首先在Program.cs中加载DbContextFactory服务:
using System;
using Microsoft.EntityFrameworkCore;
var builder=WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
//add DbContext Factory Service to DI container
builder.Services.AddDbContextFactory<BlogContext>(p=>{
p.UseSqlServer(builder.Configuration.GetConnectionString("Blogs"));
});
var app=builder.Build();
(2)通过服务的方式注入工厂:创建一个控制器FactoryContextController,在控制器中注入工厂
(3)在Action中通过注入的Factory对象的CreateDbContext()方法获取到上下文对象,其余用法同以前。注意使用using管理资源:
namespace DbContextFactoryControllers
{
public class FactoryContextController:Controller
{
private readonly IDbContextFactory<BlogsContext> _factory;
public FactoryContextController(IDbContextFactory<BlogsContext> factory)
{
_factory = factory;
}
public IActionResult Get()
{
using (var context = _factory.CreateContext())
{
// Add a Entity To Database
context.Blogs.Add(new Blog { Name = "My Blog" });
//Save the current change and commit the change to the database
context.SaveChanges();
}
return Ok();
}
}
五、避免DbContext线程处理问题
1.DbContext不是线程安全的
2.EF Core不支持在同一个DbContext实例上运行多个并行操作。因此使用异步查询或其他操作时需要使用await运算符,确保所有的异步操作完成于另一个方法调用之前。
3.当EF Core监测到尝试同时使用DbContext实例时,将会引发InvaildOpreationException
4.解决:使用async与await运算符
public async IActionResult Index()
{
using (var context = _factory.CreateContext())
{
await context.Blogs.Add(new Blog { Name = "My Blog" });
await context.SaveChanges();
}
return Ok();
}
六、DbContext连接池
1.为什么要使用DbContext连接池:为了程序的性能考虑。因为频繁的创建与销毁DbContext会消耗性能。但是DbContext是一个轻量级对象,创建及释放该对象都不涉及数据库操作,在大多数应用程序中都可以直接创建与释放该对象而不造成影响。但是在高性能程序中就需要注意性能问题。
2.使用DbContext连接池可以解决该问题。但是“池子”不是越大越好,最大为1024,一般不要超过这个大小。一旦超过了就会失效,重新走创建对象或者抛异常。
3.使用上下文池并不会立即销毁上下文对象,而是在释放上下文对象时重置对象状态并将其存储在内部池中。在下一次请求时,将直接返回该实例。
4.使用:
(1)注入连接池:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Add DbContextPool to the container
builder.Services.AddDbContextPool<BlogsContext>(p=>{
p.UseSqlServer(builder.Configuration.GetConnectionString("Blogs"));
});
(2)在控制器中直接注入使用该对象即可:用法与普通DbContext一致:
public class ContextPoolController Controller
{
private readonly BloggingContext_context:
public ContextPoolController(BloggingContext context)
{
_context =context:
}
public asyne Task<IActionResult>Index()
{
await_context.Blogs.AddAsync (new Blog(){
Url= "TestURL"});
await_context.SaveChangesAsync ()
return View();
}
}
注意:使用ContextPool不能再运行中修改连接字符串。
(3)使用非依赖注入的形式:首先创建参数对象,然后将参数对象传递给PooledDbContextOptionsBuilder<TDbContext>(options)方法。最后调用CreateDbContext方法即可
public class ContextPoolController Controller
{
public asyne Task<IActionResult>Index()
{
var options = new DbContextOptionsBuilder<BlogsContext>()
.UseSqlServer(Configuration.GetConnectionString("Blogs"));
var factory=new PooledDbContextFactory<BlogsContext>(options);
using (var context = factory.CreateContext())
{
await_context.Blogs.AddAsync (new Blog(){ Url= "TestURL"});
await_context.SaveChangesAsync ();
}
return View();
}
}