共用方式為


Using DbContext in EF 4.1 Part 12: Automatically Detecting Changes

 


The information in this post is out of date.

Visit msdn.com/data/ef for the latest information on current and past releases of EF.

For Automatic Detect Changes see https://msdn.com/data/jj556205


 

Introduction

Version 4.1 of the Entity Framework contains both the Code First approach and the new DbContext API. This API provides a more productive surface for working with the Entity Framework and can be used with the Code First, Database First, and Model First approaches. This is the last post of a twelve part series containing collections of patterns and code fragments showing how features of the new API can be used.

The posts in this series do not contain complete walkthroughs. If you haven’t used EF 4.1 before then you should read Part 1 of this series and also Code First Walkthrough or Model and Database First with DbContext before tackling this post.

Automatically detecting changes

When using most POCO entities the determination of how an entity has changed (and therefore which updates need to be sent to the database) is made by detecting the differences between the current property values of the entity and the original property values that are stored in a snapshot when the entity was queried or attached. By default, the Entity Framework does this detection automatically when the following methods are called:

  • DbSet.Find
  • DbSet.Local
  • DbSet.Remove
  • DbSet.Add
  • DbSet.Attach
  • DbContext.SaveChanges
  • DbContext.GetValidationErrors
  • DbContext.Entry
  • DbChangeTracker.Entries

Disabling automatic detection of changes

If you are tracking a lot of entities in your context and you call one of these methods many times in a loop, then you may get significant performance improvements by turning off detection of changes for the duration of the loop. For example:

 using (var context = new UnicornsContext())
{
    try
    {
        context.Configuration.AutoDetectChangesEnabled = false;

        // Make many calls in a loop
        foreach (var unicorn in myUnicorns)
        {
            context.Unicorns.Add(unicorn);
        }
    }
    finally
    {
        context.Configuration.AutoDetectChangesEnabled = true;
    }
}

Don’t forget to re-enable detection of changes after the loop—I used a try/finally to ensure it is always re-enabled even if code in the loop throws an exception.

An alternative to disabling and re-enabling is to leave automatic detection of changes turned off at all times and either call context.ChangeTracker.DetectChanges explicitly or use change tracking proxies diligently. Both of these options are advanced and can easily introduce subtle bugs into your application so use them with care.

Summary

In this part of the series we looked when DbContext automatically detects changes in your tracked entities and gave some guidance as to when this automatic change detection should be turned off.

As always we would love to hear any feedback you have by commenting on this blog post.

For support please use the Entity Framework Forum.

Arthur Vickers

Developer

ADO.NET Entity Framework

Comments

  • Anonymous
    February 07, 2011
    Hi Arthur, Could you please elaborate, why DbContext does not play well with WCF Data Services (it goes via ReflectionProvider dispite having an ObjectContext inside). Thank you in advance

  • Anonymous
    February 07, 2011
    does CTP5 support (or any) batch update or insert? thanks ashraf.

  • Anonymous
    February 07, 2011
    The comment has been removed

  • Anonymous
    February 08, 2011
    Thanks Arthur When are you planning to release the final version of CTP5? is it with VS2010 SP1? thanks, ashraf.

  • Anonymous
    February 10, 2011
    Could you please add the Code First tag to your CTP 5 series?

  • Anonymous
    February 17, 2011
    @Ashraf The plan is to relase Code First/DbContext with a Go-Live license in the first quarter of this year. @Andrey Thanks for the suggestion.

  • Anonymous
    February 17, 2011
    Any thoughts on this: social.msdn.microsoft.com/.../2fb5ceea-9f30-4665-af98-945c6485f60b ?

  • Anonymous
    March 04, 2011
    Hello, thanks for all of your work on this project. I am attempting to use it for a customer and I am stuck. Hopefully you can help. I am using DbSet.Add and then SaveChanges on the same record object multiple times. I expected it to realize it is creating the same record multiple times. The code is creating the same principal record twice, then throwing an exception, trying to create the dependent record twice with the same ID as the first principal record. Do I need to do a Find() before I Add()? [Setup] I am using ASP.NET 4, EntityFramework v4.0.30319, & SQL Server 2008 Express. I am trying to handle when a user might click "Create new record" multiple times. I have two tables in a 1-to-1 relationship, created by CodeFirst. I configured the 1-to-1 relationship using information from weblogs.asp.net/.../entity-association-mapping-with-code-first-one-to-one-shared-primary-key-associations.aspx Table #1: Persons. There will always be a person record. It is concise. Table #2: Workers. There may be a worker record. The tables are split because the # of worker fields will grow. There will  be people who are not workers. I want the Worker.ID to be the same as the Person.ID. I want the Person.ID to be a Primary Key and IDENTITY in SQL server. I want Worker.ID to be a Primary Key, but not an IDENTITY. [Domain classes] public class Person    {          public int ID { get; set; }        public virtual Worker Worker { get; set; }        ...    }    public class Worker    {        public int ID { get; set; }        public virtual Person Person { get; set; }        ...    } [Associated code #1 -- table builders]    public class PersonBuilder : EntityTypeConfiguration<Person>    {        public PersonBuilder()        {            ToTable("Persons");            HasOptional(p => p.Worker).WithRequired();        }    }    public class WorkerBuilder : EntityTypeConfiguration<Worker>    {        public WorkerBuilder() {}    } [Associated code #2 -- Test initializer]        [TestInitialize]        public void Initialize()        {            DbDatabase.SetInitializer<MyContext>(new MyInitializer());            this.DB = new MyContext();        } [Failing code: 'unit' integration test]        [TestMethod]        public void Worker_test_deduplication()        {            int reccount = 0;            //            //Arrange            Records._person2.Worker = Records._worker2;            //            //Act            try            {                DB.Persons.Add(Records._person2);                DB.SaveChanges();                DB.Persons.Add(Records._person2);                DB.SaveChanges();                DB.Persons.Add(Records._person2);                DB.SaveChanges();                reccount = DB.Persons.Count(n => n.firstname1 == Records._person2.firstname1);            }            catch (Exception e)            {                Assert.Fail(string.Format("Unexpected exception of type {0} caught: {1}",                e.GetType(), e.Message));            }            //            //Assert            Assert.IsTrue(reccount == 1, "Deduplication of records failed.");        }

  • Anonymous
    April 22, 2011
    thanks dpblogs!

  • Anonymous
    December 20, 2011
    I'm not seeing a performance gain by with: Context.Configuration.AutoDetectChangesEnabled = False Entity Framework is slower than my old data access layer that used just ADO.NET.  I want to use EF, but are there any other tips for performance?

  • Anonymous
    December 21, 2011
    The comment has been removed

  • Anonymous
    April 09, 2012
    Hello Arthur I am experiencing a problem with winforms in vs2010. When I introduce a row in a data grid, I have to enter the next row to allow Savechanges to save the just entered row. With as a consequence, that I have a new row in the database (the last one!) with empty values Another problem is when deleting a child item in a relationship, it is not deleted phisically from the DB, instead it sets the FK to the parent entity to NULL. I would like to actually delete the child phisically from the db I'm not sure how to tackle these two issues, I can imagine I'm not the first one to find these problems but I do not know how to solve them. Thanks in advance for any comment on these two issues

  • Anonymous
    April 10, 2012
    @natalia There is a walkthrough on WinForms data binding with DbContext here: msdn.microsoft.com/.../gg197523(v=vs.103).aspx. A way to get the child entities deleted is covered in that walkthrough. I'm not sure about the issue with SaveChanges, but if you have worked through the walkthrough and are still having issues I would suggest starting up a thread on Stack Overflow and I (or others) will take a look.

  • Anonymous
    October 15, 2012
    Hi Arthur! I know it's an old post, but can you please answer this question about the recommended pattern for EF Context management? stackoverflow.com/.../601179 Thanks!

  • Anonymous
    October 17, 2012
    Hi gdoron, I think the existing answers on the SO question cover it pretty well. Creating the context is pretty cheap and you're unlikely to get much benefit from caching in the normal cases. The longer your context lives, the harder it is to work with it in a known and consistent state across requests. Thanks, Arthur