Reputation: 8125
I'm experiencing what on first inspection appears to be a bug in Entity Framework 5.
I have a T4 generated DbContext and Entity classes. Note, I've modified the default T4 template a bit to support observable collections.
Lazy loading is enabled and is working fine throughout the application, except when I'm doing this:
courseEnrolment.Student.CourseEnrolments.ToList()
That is, the for the courseEnrolment I already have loaded in memory, I am accessing it's parent (Student
) and loading all of the CourseEnrolments
associated with it, which would also include the original courseEnrolment. When this happens, a second CourseEnrolment
is successfully lazily loaded into the conext (and the Local
collection) but all of it's navigation properties are null
rather than being the corresponding DynamicProxy
.
This is what the newly loaded CourseEnrolment
looks like. Note that all the Navigation Properties are null despite the normal properties being successfully loaded from the database:
And this is what a normal CourseEnrolment
looks like:
Does any one have any idea why an entity that was successfully lazily loaded is then unable to fulfil its own navigation properties by lazy loading?
UPDATE WITH MORE INFORMATION ABOUT THE SOURCE OF THE ISSUE
I've managed to recreate the issue with the following minimal code. The issue appears to be related to me observing the Local
collection.
var context = new PlannerEntities();
Debug.Assert(context.CourseEnrolments.Local.Count() == 0);
context.CourseEnrolments.Local.CollectionChanged += (sender, e) =>
{
Debug.Assert(e.NewItems.OfType<CourseEnrolment>().All(x => x.Adviser != null), "newly added entity has null navigatigon properties");
};
var c1 = context.CourseEnrolments.Single(x => x.EnrolmentId == "GA13108937");
Debug.Assert(context.CourseEnrolments.Local.Count() == 1);
Debug.Assert(context.CourseEnrolments.Local.All(x => x.Adviser != null));
c1.Student.CourseEnrolments.ToList();
Debug.Assert(context.CourseEnrolments.Local.Count() == 2);
Debug.Assert(context.CourseEnrolments.Local.All(x => x.Adviser != null),"some navigation properties were not lazy loaded");
The assertion within the CollectionChanged
handler fails which indicates at this point the navigation properties are not fulfilled. The final assertion does not fail, which would indicate at a later point, after the ObservableCollection
events have been processed the entity is fulfilled.
Any ideas how I might access navigation properties on the CollectionChanged
event of the Local
collection?
Upvotes: 2
Views: 1473
Reputation: 3796
The lazy-load proxies are not created until after the CollectionChanged event is called. Using Dispatcher causes the call to be placed on the message queue and executed some time later (how long later is not deterministic), thus the CollectionChanged event would have executed and the lazy-load proxies created, before the Debug.Assert gets called.
However, I would strongly avoid using Dispatcher for this purpose because it is non-deterministic and there is a risk of race-condition.
In order to use the CollectionChanged event like in your original code, you need change tracking proxy, not just the lazy-load proxy. For Entity Framework to generate change tracking proxies, ALL of your properties, be it of collection, object or primitive types, needs to be set as virtual.
Related issue: https://github.com/brockallen/BrockAllen.MembershipReboot/issues/290
Upvotes: 1
Reputation: 8125
Well I've managed to get it working by Dispatching the Assert call
context.CourseEnrolments.Local.CollectionChanged += (sender, e) =>
{
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>{
Debug.Assert(e.NewItems.OfType<CourseEnrolment>().All(x => x.Adviser != null), "newly added entity has null navigation properties");
}));
};
Is this the best solution?
Upvotes: 0