EF核心修改实体状态如何表现?

时间:2019-01-20 06:33:43

标签: c# entity-framework-core ef-core-2.2

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

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();
}

1 个答案:

答案 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。在顶层,它的工作方式是:

  1. DbContext为加载的每个实体制作快照
  2. 调用SaveChanges时,它将继续调用DetectChanges,这很神奇,可以找出更改或未更改的地方。
  3. 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

Entity Framework Core TrackGraph For Disconnected Data