实体状态

快照更改跟踪

首次跟踪一个实体的时候,EF Core会创建这个实体的快照。执行SaveChanges()等方法时,EF Core将会把存储在快照中的值与实体的当前值进行比较。
和DbContext有关系的实体、类都会生成快照。

实体的状态

已添加(Added):DbContext正在跟踪此实体,但数据库中尚不存在该实体。
未改变(Unchanged):DbContext正在跟踪此实体,该实体存在于数据库中,其属性值和从数据库中读取到的值一致,未发生改变。
已修改(Modified):DbContext正在跟踪此实体,并存在于数据库中,并且其部分或全部属性值已修改。
已删除(Deleted):DbContext正在跟踪此实体,并存在于数据库中,但在下次调用SaveChanges时要从数据库中删除对应数据。
已分离(Detached):DbContext未跟踪该实体。

查询实体状态

使用DbContext的Entry()方法来获取实体在EF Core的跟踪信息对象EntityEntry。EntityEntry类的State属性代表实体的状态,通过DebugView.LongView属性可以看到实体的变化信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
static async Task Main(string[] args) {
using (var ctx = new MyDbContext()) {
var items = ctx.Articles.Take(5).ToArray();
var a1 = items[0];
var a2 = items[1];
var a3 = items[2];

var a4 = new Article() { Title = "ddd", Message = "xxxxxxxxxxx" };
var a5 = new Article() { Title = "阿斯拉达覅见哦", Message = "送到哪给下次" };

a2.Title = "WBG 0:3 不敌 T1";
ctx.Articles.Remove(a3);
ctx.Articles.Add(a4);

EntityEntry e1 = ctx.Entry(a1);
EntityEntry e2 = ctx.Entry(a2);
EntityEntry e3= ctx.Entry(a3);
EntityEntry e4 = ctx.Entry(a4);
EntityEntry e5 = ctx.Entry(a5);

Console.WriteLine(e1.State);
Console.WriteLine(e2.DebugView.LongView);
Console.WriteLine(e3.State);
Console.WriteLine(e4.State);
Console.WriteLine(e5.State);

ctx.SaveChanges();
}
}

结论

DbContext会根据跟踪的实体的状态,在SaveChanges()的时候,根据实体状态的不同,生成Update、Delete、Insert等SQL语句,来把内存中实体的变化更新到数据库。

AsNoTracking

如果通过DbContext查询出来的对象只是用来展示,不会发生状态改变,则可以使用AsNoTracking()来“禁用跟踪”,可以降低内存占用。

1
2
3
4
5
6
7
8
static async Task Main(string[] args) {
using (var ctx = new MyDbContext()) {
var items = ctx.Articles.AsNoTracking().Take(5).ToArray();
foreach (var item in items) {
await Console.Out.WriteLineAsync(item.Title);
}
}
}