Entity Framework - "Attach ()" is slow

I use EF5 and attach a disabled POCO object graph to my context, something like this: -

using (var context = new MyEntities()) { context.Configuration.AutoDetectChangesEnabled = false; context.MyEntities.Attach(myEntity); // Code to walk the entity graph and set each entity state // using ObjectStateManager omitted for clarity .. context.SaveChanges(); } 

The entity "myEntity" is a large entity graph, with many child collections, which in turn have their own child collections, etc. The entire graph contains about 10,000 objects, but only a small number usually change.

The code for setting entity states and actual SaveChanges() pretty fast (<200ms). This is Attach() , that the problem is here, and takes 2.5 seconds, so I was wondering if this could be improved. I saw articles that tell you to set AutoDetectChangesEnabled = false , which I do above, but that doesn't make any difference in my script. Why is this?

+6
source share
1 answer

I am afraid that 2.5 seconds to bind an object graph with 10,000 objects is "normal". This is probably a snapshot of the object that happens when you attach a graph that takes this time.

If β€œonly a small number usually changes” - say, 100 - you could consider loading the source objects from the database and changing their properties instead of attaching the entire graph, for example:

 using (var context = new MyEntities()) { // try with and without this line // context.Configuration.AutoDetectChangesEnabled = false; foreach (var child in myEntity.Children) { if (child.IsModified) { var childInDb = context.Children.Find(child.Id); context.Entry(childInDb).CurrentValues.SetValues(child); } //... etc. } //... etc. context.SaveChanges(); } 

Despite the fact that this will create many queries to one database, only β€œflat” objects without navigation properties that are connected (when calling Find ) will not consume much time will be downloaded and added. To reduce the number of requests, you can also try loading objects of the same type as the "package" using the Contains request:

  var modifiedChildIds = myEntity.Children .Where(c => c.IsModified).Select(c => c.Id); // one DB query context.Children.Where(c => modifiedChildIds.Contains(c.Id)).Load(); foreach (var child in myEntity.Children) { if (child.IsModified) { // no DB query because the children are already loaded var childInDb = context.Children.Find(child.Id); context.Entry(childInDb).CurrentValues.SetValues(child); } } 

This is just a simplified example under the assumption that you only need to change the scalar properties of objects. It can become arbitrarily complex if changes in relationships are involved (children have been added and / or removed from collections, etc.).

+2
source

Source: https://habr.com/ru/post/943976/