EF Core 修改后的实体状态如何表现?
How does EF Core Modified Entity State behave?
我们将实体 state = modified 放在更改之后还是进行更改之前重要吗?
using (var db = new LakshyaContext())
{
foreach (var category in db.Categories)
{
db.Entry(category).State = EntityState.Modified; // before
category.Count = 25; //Making Changes
db.Entry(category).State = EntityState.Modified; //After
}
db.SaveChanges();
}
所以首先,让我们把最重要的事情放在一边:
你是对的。在您的示例中,您不需要手动调用 db.Entry(category).State = EntityState.Modified
。这是因为您正在从上面的上下文中加载条目(类别)。这被称为“连接场景”,其中 DbContext
知道实体, 跟踪它们。这是相同的,例如在 ASP.NET 核心应用程序中,上下文在 HTTP 请求中共享。
您在 using (var db = new LakshyaContext())
范围内所做的任何修改都会在您调用 SaveChanges
时通过上下文获知。
现在,在处理断开连接的场景(如您所说的 UnTracked 实体)时,我们必须更深入地挖掘。
要理解这一点,首先您需要知道 DbContext
如何知道发生了什么变化。举个例子:
using (var context = new MyContext())
{
// loads the book by it's ISBN
var book = context.Books
.Single(p => p.ISBN == "123456");
// Do changes
book.Price = 30;
// Save changes
context.SaveChanges();
}
它怎么知道 Price
改变了?因为它只是 Book
class 上的普通汽车 属性? DetectChanges
方法背后的魔法。
在某些特定情况下,DbContext
调用 DetectChanges
方法。最明显的一个是调用 SaveChanges
时。在顶层,它的工作方式是:
DbContext
为其加载的每个实体制作快照
- 当
SaveChanges
被调用时,它会继续调用 DetectChanges
,这将很神奇地找出发生了什么变化。
DbContext
然后负责将正确的命令发送到数据库。
至此,我们知道DetectChanges
的责任了。现在重要的部分是知道何时调用 DetectChanges
(除了我们已经知道的 SaveChanges)。这对于最终回答您的“订单”问题至关重要。来自 Arthur Vickers
的链接文章
The methods that call DetectChanges:
- DbSet.Find
- DbSet.Local
- DbSet.Remove
- DbSet.Add
- DbSet.Attach
- DbContext.SaveChanges
- DbContext.GetValidationErrors
- DbContext.Entry
- DbChangeTracker.Entries
让我们检查一下演示“断开连接”场景的代码。
public Task UpdateBook()
{
Book book = null;
// Just loads the book from this context
using (var context = new MyContext())
{
book = context.Books
.Single(p => p.ISBN == "123456");
}
// Starts a new context where the book is going to be updated
using (var anotherContext = new MyContext())
{
// Changed the price - remember this is not loaded from this context!
book.Price = 40;
// THIS IS KEY: This will call `DetectChanges`
// This entity will be tracked by the context now
anotherContext.Entry(book).State = EntityState.Modified
// Update will occur normally
anotherContext.SaveChanges();
}
}
当我们进入第二个 DbContext,
时,它并不知道我们的 book
实体。我们更改价格,然后调用 db.Entry(book).State = EntityState.Modified
。此时,DbContext
将开始跟踪它,并调用 DetectChanges
。继续调用 SaveChanges
将按预期工作。
如果我们做相反的事情,在实际改变价格之前调用 db.Entry(book).State = EntityState.Modified
事情会......仍然有效!
为什么?好吧,使用 db.Entry(book).State
手动更改实体的状态会将实体添加到上下文中,这意味着它将开始跟踪它的更改。
因此,即使我们调用 db.Entry(book).State
然后在实体上应用更改也没有关系,因为最后调用 SaveChanges
会再次触发 DetectChanges
, 因为它之前已经被调用过,所以已经有实体的快照。
您可以自己验证此行为的一种方法是 运行 上面的代码为 DbContext
:
启用了日志记录
// Calling db.Entry.. produces this log:
DetectChanges starting for 'MyContext'.
Microsoft.EntityFrameworkCore.ChangeTracking:Debug: DetectChanges completed for 'MyContext'.
Context 'MyContext' started tracking 'Book' entity.
// Calling SaveChanges produces this log:
SaveChanges starting for 'MyContext'
DetectChanges starting for 'MyContext'.
DetectChanges completed for 'MyContext'.
Opening connection to database 'BooksDB'
Beginning transaction with isolation
...
现在说几点:
以上在断开连接的情况下的更新将在 table 中的 ALL COLUMNS 上发布更新。这可能不是您所期望的。有一些方法可以防止这种情况。 Read more here
DetectChanges
在内部做了很多事情,而不仅仅是对更改应用合并。它负责处理外键、更新导航属性的引用等,并进行“修复”。
更多资源可供阅读:(尤其是 Arthur Vickers 的资源!)
Secrets of DetectChanges Part 1: What does DetectChanges do?
Secrets of DetectChanges Part 2: When is DetectChanges called automatically?
Possible Issue with Change Tracker Caching Entity State EF Core 2.0.2
Working with Disconnected Entity Graph in Entity Framework Core
我们将实体 state = modified 放在更改之后还是进行更改之前重要吗?
using (var db = new LakshyaContext())
{
foreach (var category in db.Categories)
{
db.Entry(category).State = EntityState.Modified; // before
category.Count = 25; //Making Changes
db.Entry(category).State = EntityState.Modified; //After
}
db.SaveChanges();
}
所以首先,让我们把最重要的事情放在一边:
你是对的。在您的示例中,您不需要手动调用 db.Entry(category).State = EntityState.Modified
。这是因为您正在从上面的上下文中加载条目(类别)。这被称为“连接场景”,其中 DbContext
知道实体, 跟踪它们。这是相同的,例如在 ASP.NET 核心应用程序中,上下文在 HTTP 请求中共享。
您在 using (var db = new LakshyaContext())
范围内所做的任何修改都会在您调用 SaveChanges
时通过上下文获知。
现在,在处理断开连接的场景(如您所说的 UnTracked 实体)时,我们必须更深入地挖掘。
要理解这一点,首先您需要知道 DbContext
如何知道发生了什么变化。举个例子:
using (var context = new MyContext())
{
// loads the book by it's ISBN
var book = context.Books
.Single(p => p.ISBN == "123456");
// Do changes
book.Price = 30;
// Save changes
context.SaveChanges();
}
它怎么知道 Price
改变了?因为它只是 Book
class 上的普通汽车 属性? DetectChanges
方法背后的魔法。
在某些特定情况下,DbContext
调用 DetectChanges
方法。最明显的一个是调用 SaveChanges
时。在顶层,它的工作方式是:
DbContext
为其加载的每个实体制作快照- 当
SaveChanges
被调用时,它会继续调用DetectChanges
,这将很神奇地找出发生了什么变化。 DbContext
然后负责将正确的命令发送到数据库。
至此,我们知道DetectChanges
的责任了。现在重要的部分是知道何时调用 DetectChanges
(除了我们已经知道的 SaveChanges)。这对于最终回答您的“订单”问题至关重要。来自 Arthur Vickers
The methods that call DetectChanges:
- DbSet.Find
- DbSet.Local
- DbSet.Remove
- DbSet.Add
- DbSet.Attach
- DbContext.SaveChanges
- DbContext.GetValidationErrors
- DbContext.Entry
- DbChangeTracker.Entries
让我们检查一下演示“断开连接”场景的代码。
public Task UpdateBook()
{
Book book = null;
// Just loads the book from this context
using (var context = new MyContext())
{
book = context.Books
.Single(p => p.ISBN == "123456");
}
// Starts a new context where the book is going to be updated
using (var anotherContext = new MyContext())
{
// Changed the price - remember this is not loaded from this context!
book.Price = 40;
// THIS IS KEY: This will call `DetectChanges`
// This entity will be tracked by the context now
anotherContext.Entry(book).State = EntityState.Modified
// Update will occur normally
anotherContext.SaveChanges();
}
}
当我们进入第二个 DbContext,
时,它并不知道我们的 book
实体。我们更改价格,然后调用 db.Entry(book).State = EntityState.Modified
。此时,DbContext
将开始跟踪它,并调用 DetectChanges
。继续调用 SaveChanges
将按预期工作。
如果我们做相反的事情,在实际改变价格之前调用 db.Entry(book).State = EntityState.Modified
事情会......仍然有效!
为什么?好吧,使用 db.Entry(book).State
手动更改实体的状态会将实体添加到上下文中,这意味着它将开始跟踪它的更改。
因此,即使我们调用 db.Entry(book).State
然后在实体上应用更改也没有关系,因为最后调用 SaveChanges
会再次触发 DetectChanges
, 因为它之前已经被调用过,所以已经有实体的快照。
您可以自己验证此行为的一种方法是 运行 上面的代码为 DbContext
:
// Calling db.Entry.. produces this log:
DetectChanges starting for 'MyContext'.
Microsoft.EntityFrameworkCore.ChangeTracking:Debug: DetectChanges completed for 'MyContext'.
Context 'MyContext' started tracking 'Book' entity.
// Calling SaveChanges produces this log:
SaveChanges starting for 'MyContext'
DetectChanges starting for 'MyContext'.
DetectChanges completed for 'MyContext'.
Opening connection to database 'BooksDB'
Beginning transaction with isolation
...
现在说几点:
以上在断开连接的情况下的更新将在 table 中的 ALL COLUMNS 上发布更新。这可能不是您所期望的。有一些方法可以防止这种情况。 Read more here
DetectChanges
在内部做了很多事情,而不仅仅是对更改应用合并。它负责处理外键、更新导航属性的引用等,并进行“修复”。
更多资源可供阅读:(尤其是 Arthur Vickers 的资源!)
Secrets of DetectChanges Part 1: What does DetectChanges do?
Secrets of DetectChanges Part 2: When is DetectChanges called automatically?
Possible Issue with Change Tracker Caching Entity State EF Core 2.0.2
Working with Disconnected Entity Graph in Entity Framework Core