entity-frameworkcollectionsnullreferenceexceptionanonymousfixup

EF anonymous object query returns null collections instead of empty ones


I'm using this trick to perform conditional Include's with EF. http://blogs.msdn.com/b/alexj/archive/2009/10/13/tip-37-how-to-do-a-conditional-include.aspx

The problem I'm having is that any collections that don't have records, are null, and not empty. This is causing headaches cos I have to check each collection before I can loop through it in my mvc view, otherwise i get a null reference exception.

For example, the StudentModules collection will be null. How can I turn it into an empty list in my query? ie without having to loop through it all and checking.

I can put a constructor in the poco to initialize the list, which fixes it, but the this collection is a virtual member in the poco (based on an EF video!) - surely this is not the way to go?

var query = from module in db.Modules
            where module.Id == id
            select new 
            {
              module,
              QualificationModules = from qualificationModule in module.QualificationModules
                                     where qualificationModule.IsDeleted == false
                                     select new
                                     {
                                       qualificationModule,
                                       qualificationModule.Qualification,
                                       StudentModules = from studentModule in qualificationModule.StudentModules
                                                        where studentModule.IsDeleted == false 
                                                        select new
                                                        {
                                                          studentModule,
                                                          studentModule.Student
                                                        }
                                     },

              Assessments = (from assessment in module.Assessments
                             where assessment.IsDeleted == false
                             select new
                             {
                               assessment,
                               assessment.AssessmentType
                             }
                            )
            };

var modules = query.AsEnumerable().Select(x => x.module);

return modules.ToList().First();

Solution

  • Relationship fixup runs when an entity gets attached to a context - either manually by calling Attach or when the entity is materialized as a result of a query (your case).

    It is based on foreign keys of an entity and works in both directions:

    As a side note: The fact that relationship fixup is based on foreign keys (they are always loaded when you query an entity, no matter if the FK is exposed as property in the model class or not) is also the reason why relationship fixup does not apply to and does not work for many-to-many relationships because the two entities of a many-to-many relationship don't have a foreign key.

    Now, if there are no related StudentModules in your case there is no StudentModule entity that gets loaded into the context and there is nothing what EF could target for a fixup. Keep in mind that the fixup algorithm is not related to a particular query and does not only fix relationships between entities that this query would materialize but it will consider all entities for fixup that the context already contains, no matter how they came into the context. If you would want that collections get instantiated as empty collections EF had run through all attached parent entities of StudentModules and just create an empty collection. It makes no sense to do this during fixup instead of creating empty collections up-front before entities get attached to a context.

    I can put a constructor in the poco to initialize the list, which fixes it, but the this collection is a virtual member in the poco (based on an EF video!) - surely this is not the way to go?

    In my opinion it is the best solution if you don't want to have null collections in your model class instances. It doesn't matter if the collection is declared as virtual (to enable lazy loading) or not. A collection type does not have a derived proxy type, only the instances that get added to the collection are derived proxies. In both case you can just use StudentModules = new HashSet<StudentModule>(); (or List if you prefer).