EF核心修改实体状态如何表现? [英] How does EF Core Modified Entity State behave?
问题描述
在更改之后或更改之前,我们将实体状态=修改是否重要?
Does it matter we put the entity state = modified after changes or before making changes?
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();
}
推荐答案
因此,首先让我们摆脱最重要的事情:
So first, let's get the most important thing out of the way:
你是对的.在您的示例中,您无需手动调用 db.Entry(category).State = EntityState.Modified
.这是因为您要从上面的上下文中加载条目(类别).这被称为连接场景". DbContext
知道这些实体的地方,它是跟踪它们.这是相同的,例如在ASP.NET Core应用程序中,该上下文在HTTP请求之间共享上下文.
You are right. In your example, you don't need to manually call db.Entry(category).State = EntityState.Modified
. This is because you are loading the entries (categories) from the context above. This is known as the "Connected Scenario" where the DbContext
is aware of the entities, it's tracking them. This is the same, for instance in an ASP.NET Core app, where the context is shared across the HTTP request.
在调用 SaveChanges
时,上下文会知道您在使用(var db = new LakshyaContext())
的范围之间进行的任何修改.
Any modification you make between the scope of using (var db = new LakshyaContext())
, will be known by the context when you call SaveChanges
.
现在,当处理断开连接的场景(如您所说的UnTracked实体)时,我们必须进行更深入的研究.
Now, when working on disconnected scenarios (as you said UnTracked entities), we have to dig a little bit deeper.
要了解这一点,首先,您需要了解 DbContext
是如何知道更改的.请看以下示例:
To understand that, first you need to know how the DbContext
know what's changed. Take the following example:
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
类上的普通自动属性?神奇之处在于 DetectChanges
方法.
How does it know that the Price
changed? since it's just a normal auto property on the Book
class? The magic lies behind the DetectChanges
method.
在某些特定情况下, DbContext
调用 DetectChanges
方法.最明显的是调用 SaveChanges
.在顶层,它的工作方式是:
In some specific cases, the DbContext
calls the DetectChanges
method. The most obvious one is when SaveChanges
is called. In a top level, the way it works is:
-
DbContext
为其加载的每个实体制作快照 - 当调用
SaveChanges
时,它将继续调用DetectChanges
,这将很神奇地找出更改或未更改的地方. -
DbContext
然后负责将正确的命令发送到数据库.
- The
DbContext
makes a snapshot of each entity it loads - When
SaveChanges
is called, it will proceed to callDetectChanges
which will do it's magic to figure it out what's changed or not. DbContext
then takes care of sending the correct commands to the db.
至此,我们知道了 DetectChanges
的责任.现在重要的部分是知道何时调用 DetectChanges
(除了我们已经知道的SaveChanges).这对于最终回答您的订单"至关重要.问题.摘自 Arthur Vickers
At this point, we know the responsibility of DetectChanges
. The important part now is knowing when DetectChanges
is called (apart from SaveChanges that we already know). This is crucial to finally answer your "Order" question. From the linked article from Arthur Vickers
调用DetectChanges的方法:
- DbSet.Find
- DbSet.Local
- DbSet.Remove
- DbSet.Add
- DbSet.Attach
- DbContext.SaveChanges
- DbContext.GetValidationErrors
- DbContext.Entry
- DbChangeTracker.Entries
让我们检查一下该代码,该代码演示了断开连接"的情况.场景.
Let's examine this code that demonstrates the "disconnected" scenario.
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
将按预期工作.
When we go into the second DbContext,
it is not aware of our book
entity. We change the price and then call db.Entry(book).State = EntityState.Modified
. At this point, the DbContext
will start tracking it, and DetectChanges
is invoked. Proceeding calling SaveChanges
will work as expected.
如果我们做相反的事情,则在实际更改价格之前调用 db.Entry(book).State = EntityState.Modified
还是可以的!
If we had done the opposite, calling db.Entry(book).State = EntityState.Modified
before actually changing the price things would.... still work!
为什么?好吧,使用 db.Entry(book).State
手动更改实体的状态会将实体添加到上下文中,这意味着它将开始跟踪其更改.因此,即使我们调用 db.Entry(book).State
然后在实体上应用更改也没关系,因为最后调用 SaveChanges
会触发 DetectChanges
,并且由于之前已被调用,因此该实体已经存在一个快照.
Why? Well, manually changing the state of the entity with db.Entry(book).State
will add the entity to the context, meaning it will start tracking it for changes.
So, even if we call db.Entry(book).State
and then apply changes on the entity it will not matter because calling SaveChanges
at the end, will trigger again DetectChanges
, and since it was already called before, there was already a snapshot in place for the entity.
您可以自己验证此行为的一种方法是运行上面的代码,并为 DbContext
启用日志记录:
One way you can verify this behavior yourself is running the code above with logging enabled for the 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
...
现在有一些评论:
在断开连接的情况下,以上更新将在表中的所有列上发布更新.这可能不是您所期望的.有防止这种情况的方法.在此处了解更多信息
The update above in the disconnected scenario will issue an update on ALL COLUMNS in the table. This might not be what you expected. There are ways to prevent this. Read more here
DetectChanges
在内部做很多事情,不仅对变更应用合并.它负责处理外键,更新导航属性的引用等,并进行修复".
DetectChanges
does a lot of stuff internally, not only applying merges on changes. It takes care of Foreign Keys, updating references of navigation properties and more, and doing "fixup".
有更多资源需要阅读:(尤其是亚瑟·维克斯(Arthur Vickers)的资源!)
More resources to read on: (especially the ones from Arthur Vickers!)
秘密的ChangeChanges第1部分:DetectChanges的作用是什么?
DetectChanges的秘密第2部分:何时自动调用DetectChanges?
变更跟踪器缓存实体状态EF Core 2.0.2可能出现的问题
这篇关于EF核心修改实体状态如何表现?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!