在更改之后或更改之前,我们将实体状态=修改是否重要?
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();
}
答案 0 :(得分:14)
首先,让我们摆脱最重要的事情:
你是对的。在您的示例中,您无需手动调用db.Entry(category).State = EntityState.Modified
。这是因为您要从上面的上下文中加载条目(类别)。这就是所谓的“关联方案”,其中DbContext
知道实体,它正在跟踪它们。这是相同的,例如在ASP.NET Core应用程序中,该上下文在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
类的常规自动属性?神奇之处在于DetectChanges
方法的背后。
在某些特定情况下,DbContext
调用DetectChanges
方法。最明显的一个是何时调用SaveChanges
。在顶层,它的工作方式是:
DbContext
为加载的每个实体制作快照SaveChanges
时,它将继续调用DetectChanges
,这很神奇,可以找出更改或未更改的地方。DbContext
然后负责将正确的命令发送到数据库。至此,我们知道了DetectChanges
的责任。现在重要的部分是知道何时调用DetectChanges
(除了我们已经知道的SaveChanges)。这对于最终回答您的“订单”问题至关重要。来自Arthur Vickers
调用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
db.Entry(book).State = EntityState.Modified
// Update will occur normally
db.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
...
现在有一些评论:
在断开连接的情况下,以上更新将在表中的所有列上发布更新。这可能不是您所期望的。有防止这种情况的方法。 Read more here
DetectChanges
在内部做很多事情,不仅对变更应用合并。它负责处理外键,更新导航属性的引用等,并进行“修复”。
更多资源可供阅读:(尤其是来自亚瑟·维克斯的资源!)
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