在EFCore中的CRUD
在EFCore中的CRUD
实体的几种状态
一个实体可能会会有以下几种状态:
Added
: 实体在数据库中不存在。SaveChanges
方法将发起INSERT
语法。Unchanged
:SaveChanges
方法对这种状态的实体什么都不会做。当从数据库读取实体之后,实体就是这种状态。Modified
: 实体中全部或者部分字段是这种状态时,SaveChanges
方法将发起UPDATE
语法。Deleted
: 实体被标志位删除时,SaveChanges
方法将发起DELETE
语法。Detached
: 实体没有被数据库上下文(database context)状态跟踪。
更改实体状态的方法
- 使用
Context.Entry
:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16//1. 修改实体
Student studentToDelete = new Student() { ID = id };
_context.Entry(studentToDelete).State = EntityState.Deleted;
await _context.SaveChangesAsync();
//2. 修改字段
//查询 id=1 的学生
var studentToUpdate = await _context.Students.SingleOrDefaultAsync(s => s.ID == 1);//EntityState=Unchanged
var studentEntry = _context.Entry(studentToUpdate);
//设置为修改状态
studentEntry.Property(s => s.LastName).IsModified = true;//EntityState=Modified
studentEntry.Property(s => s.FirstMidName).IsModified = true;
//保存更改
await _context.SaveChangesAsync();
- 使用
防止加载不必要的字段
BindAttribute
:Bind["Course","Grade"] Student s
。较适用于Creat
。- 使用
TryUpdateModel
,传入一组委托参数,指定要更新的字段。这个方法会从Form表单读取数据,较适用于Edit
。 - 使用
View Model
,适用于Creat
和Edit
场景。做法:将view model 作为类型参数,接收数据,然后将其所有属性复制(可使用AutoMapper
)到要更改的类实例中,使用_context.Entry
方法将实体状态更改为Unchanged
,然后再逐个将需要应用修改的属性状态IsModified
改为true
。最终保存到数据库。
主要
方法1 不适用于Edit
方法,Bind
会生成一个实例,将所有字段设置状态为Modified
,未指定的字段会被清空。对于Edit
方法,推荐先读取,再使用TryUpdateModel
增删改
1. 先读实体再修改
1 | [HttpPost, ActionName("Delete")] |
1 | [HttpPost, ActionName("Edit")] |
2. 先创建实体再修改
1 | [HttpPost] |
1 | public async Task<IActionResult> Edit(int id, [Bind("ID,EnrollmentDate,FirstMidName,LastName")] Student student) |
3. 使用ViewModel
创建一个具有几个需要更改属性的 `ViewModel`
查询
No-tracking
查询HttpGet
方法,在上下文生命周期内(默认是Scoped
),不需要修改任何实体,不用通过多个查询加载导航属性- 需要查询大量数据,仅有少量需要修改,禁用跟踪能大幅提高效率。在需要更新数据之前,先查询这些少量的数据,再更新。
- 前面已经查询出对应数据,上下文已经跟踪该数据,但想使用
attach
方式更新该数据。如果未禁用跟踪,无法以这种方式更新
跟踪查询(默认方式)
在上下文生命周期内(默认是
Scoped
),数据会缓存,并且一直与数据库同步。
其他
- 数据库连接什么时候释放
数据库上下文是由DI
注入,AddDbContext
默认生命周期为Scope
,生命周期之后,最终会释放数据库连接等资源。Scope
意味着一次请求结束,生命周期结束。为什么?这里的Scope
很有可能是HttpContext.RequestServices
这个ServiceProvider
创建的,或者其子ServiceProvider
创建的,而HttpContext
每次请求结束会释放,so… - 多个修改之后
SaveChanges
,EFCore 隐式实现事务。