简体   繁体   中英

Navigation property incompletely loaded after eager load

lI use a code looking like this one to select a large list of objects, for read only purpose :

using (DBContext db = new MyContextClass())
{
    ... data creation ... 
    db.SaveChanges();
}

using (DBContext db = new MyContextClass())
{
     db.Configuration.LazyLoadingEnabled = false;
     db.Configuration.ProxyCreationEnabled = false;
     db.Configuration.AutoDetectChangesEnabled = false;

     DbQuery data = db.Set(someType)
     foreach (string propertyName op in somePropertieNames)
                data = data.Include(propertyName);

     foreach (object item in data.AsNoTracking())
         ScanNavigatorsOf(item);
}

in ScanNavigatorsOf() , I read all the navigation properties, and I noticed that the first object yielded by the foreach loop has incomplete collections and references. It seems that the population of the navigation properties is not finished at the time the program hits my ScanNavigatorsOf(...) method. All the other objects have complete navigation properties. I am running unit tests on this, so I can ensure that objects are well stored in the database.

How can I wait that the navigation collections and references of the loaded objects are fully populated?

Like explained in ObjectContext.ObjectMaterialized Event , the collections seems not materialized at the same time as the main object, but How can I know when the process is done?

First load all data by using ToList() then scan navigation properties:

var queryableData = db.Set(someType).AsNoTracking();
var data = somePropertieNames.Aggregate(queryableData , (current, property) => current.Include(property)).ToList();
foreach (object item in data)
         ScanNavigatorsOf(item);

I'm pretty sure you're experiencing side effects of turning off DetectChanges . This is a call EF makes many times below the surface to ensure that all associations between tracked entities have been established correctly and match with primitive foreign key values (this is called relationship fixup ). There may be reason for you to turn it off, but as Lerman & Miller's say in their book DbContext :

Working out when DetectChanges needs to be called isn't as trivial as it may appear. The Entity Framework team strongly recommends that you only swap to manually calling DetectChanges if you are experiencing performance issues. It's also recommended to only opt out of automatic DetectChanges for poorly performing sections of code and to reenable it once the section in question has finished executing.

On top of this, you fetch the objects without tracking. Now EF is almost completely crippled when it comes to associations.

You should either let DetectChanges do its work, or call it in the loop. And enable tracking, otherwise there's nothing the change tracker can do to fix associations.

 foreach (object item in data)
 {
     db.ChangeTracker.DetectChanges();
     ScanNavigatorsOf(item);
 }

Or turn AutoDetectChangesEnabled on before the loop runs, when in ScanNavigatorsOf one of these many methods triggering DetectChanges will run.

But there's another gotcha in AsNoTracking . The effect of switching off change tracking is that the context can't serve as an identity map , which means: it can't make sure that each record in the database will be materialized exactly once.

If there is a 1-n-1 association in your Includes , say Order-OrderLine-Product , without tracking for each OrderLine a new Product instance will be created, even if the "same" product was already created for a previous order line. This is not a big issue as long as you're using the data read-only, but as soon as you're going to attach them to the context (eg for replication) you'll run into duplicate key exceptions.

In short: when associations matter, let the change tracker do its work and make sure DetectChanges is called in time.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM