Question

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();
Was it helpful?

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:

  • If the context already contains an entity A with a foreign key f to entity B and an entity B is being attached to the context that has a primary key with the same value f as the foreign key in A (i.e. the two entities are related by an FK relationship) then Entity Framework will do the following:

    • If A has a navigation reference property to B it will assign the attached entity B to this property.
    • If B has a navigation reference property to A (one-to-one relationship) it will assign A to this property.
    • If B has a navigation collection property to A (one-to-many relationship) it will add A to this collection in the attached entity B. If the collection is null it will instantiate the collection before adding.
  • If an entity B is being attached to the context that has a foreign key f to an entity A that the context already contains and that has f as primary key EF will set the navigation properties based on the same rules like above.

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).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top