1

I'm not much of a DB guy and I'm pretty new to EF. I am confused by the erratic behavior of lazy loading on FK relationships.

Let's say I have a table Parent and a table Children. Children has a non-unique FK into Parent. Now some code:

Parent GetParent(int parentId)
{
    return Entities.Parents.Single(p => p.ParentId == parentId);
}

Child GetChildByName(int parentId, string name)
{
    var parent = GetParent(parentId);
    return parent.Children.Single(c => c.Name == name);   
}

Now, sometimes the call to parent.Children.Single will fail because the sequence Parent.Children is empty. Not always, but sometimes, which makes this terribly frustrating. When it does fail I have verified via Intellitrace that no SQL call to fetch Children has executed.

This relationship is already created correctly in the database and all input parameters are correct. If I use Include("Children") and eagerly load all Children it will of course work every time.

If I throw a Thread.Sleep(1000) in after I get the Parent but before I filter for a Child, the Children will more often than not be loaded and the call succeeds.

So, as far as I can tell, this is a timing related issue. FK relationships are not being loaded on demand, but they are being loaded at seemingly arbitrary times. I don't understand this behavior and I assume that I must be missing something.

I really don't want to add Include("SomeFK") everywhere as it makes my code more brittle and, really, why should I have to do that? If I have to sprinkle calls to Include all over the place I am

A) Grabbing data I often don't need B) Writing code which I must edit whenever I add or remove relationships C) Not getting much (aside from trivial code generation) out of my ORM. I may as well be writing raw SQL calls at this point.

So yeah, I must be missing something, but I've looked and have not been able to find a comprehensive explanation of lazy loading using POCO types (database first!) for EF.

4

2 回答 2

0

I wonder if this has to do with the Entities object you have in the Parent method. Could it be that the Entities object is disposed or unavailable so that, while the Parent object is grabbed, the child is not because the Entity is disposed before the Lazy Loading can take place?

I'd recommend with a code pattern like this to get your objects with explicit loading if you're going to retrieve them outside of the scope of the Entity Context. Otherwise with the context gone, the object will be unable to lazy load? Not 100% on this but it's why I always explicitly load when I am returning records without the DbContext object available.

于 2013-05-09T20:26:19.957 回答
0

Ok, so this was my own dumb fault. Of course, the bug was in my code, not a library used by thousands of developers.

I didn't provide enough information in the question. I’ll leave out the minor details, but the root cause is a race condition.

I am creating/loading entity objects via a context created on thread A. They are then passed to functions which spool up a new thread (B) and do their thing. The call to the navigation property is eventually performed on thread B.

It is clear from reading the documentation that contexts are not thread safe. From what I can glean, EF is performing lazy loading on the thread that the context was created on, i.e., thread A.

This is why the Thread.Sleep() seems to “fix” the issue; thread B is idling for a second while the main thread gets a chance to fill in the nav properties.

In our code, the main thread is busy performing a bunch of UI updates right after the call to the asynchronous function is called, so it blocks the context from doing its thing. Performing everything synchronously works just fine.

Hopefully this helps someone who runs into a similar situation someday. As the old adage goes, RTFM.

于 2013-05-15T18:55:04.983 回答