EF核心修改实体状态如何表现? [英] How does EF Core Modified Entity State behave?

查看:75
本文介绍了EF核心修改实体状态如何表现?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在更改之后或更改之前,我们将实体状态=修改是否重要?

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:

  1. DbContext 为其加载的每个实体制作快照
  2. 当调用 SaveChanges 时,它将继续调用 DetectChanges ,这将很神奇地找出更改或未更改的地方.
  3. DbContext 然后负责将正确的命令发送到数据库.
  1. The DbContext makes a snapshot of each entity it loads
  2. When SaveChanges is called, it will proceed to call DetectChanges which will do it's magic to figure it out what's changed or not.
  3. 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可能出现的问题

在实体框架中使用断开的实体图核心

用于断开数据的实体框架核心TrackGraph

这篇关于EF核心修改实体状态如何表现?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆