Search code examples
c#asp.net-mvcentity-frameworkormtable-per-type

Load EF navigation property in a Table per Type Inheritance scenario


I have a scenario very similar to this question but I am trying to to something a bit more complex.

To recap, I basically have a List of Cases, each one of a different type:

Case -> CaseA
Case -> CaseB
Case -> CaseC

Every derived Case class has one or more navigational properties which I need to include:

Case -> CaseA -> Employee
Case -> CaseB -> List<Something>
Case -> CaseC -> List<SomethingElse>

Now, of course I could do a massive switch statement but I am looking for something clever like this:

foreach(var myCase in ListOfCases) 
{
    context.LoadAll(myCase); // <- THIS!
    context.Entry(myCase).LoadAllProperties() // <- OR THIS...
    // etc. etc.
}

Of course the methods do not exist so I was wondering if anyone has ever had a similar problem and what could be a nice and clever way to solve it.

Thanks!


Solution

  • In the end the solution was really simple: do nothing! :)

    Basically if the object hierarchy is set up properly and the navigational properties (and collections) all have the virtual modifier so that LazyLoading could be enabled, EF will dive into the object hierarchy on its own to load properties that are not loaded during the first SELECT:

     public CasesDbContext() {
         Configuration.LazyLoadingEnabled = true;
         Configuration.ProxyCreationEnabled = true;
     }
    

    and then for example this is the method:

    var result = new List<CaseViewModel>();
    var cases = _casesRepository.All;
    foreach (var customCase in cases) {
        result.Add(new CaseViewModel() {
            Complete = customCase.IsComplete(), // <- at this point, the customCase is
                                                // the derived implementation
                                                // but the full hierarchy is missing
        });
    }
    

    and this is an example derived class:

    public class CaseB : Case {
        public int ReferenceId { get; set; }
        public virtual Reference Reference { get; set; } // <- "virtual" here is important!
    
        protected override bool IsComplete() {
            return Reference.Name == "Tallmaris"; // <- at this point the EF 
                                                  // will load the Reference.
        }
    }
    

    The other caveat is that loading a reference while iterating through a set of entities may give an error like There is already an open DataReader associated with this Command which must be closed first. The solution is either to use ToList() before iterating or enable MultipleActiveResultSets in the connection string (credits to @Ladislav for his answer here).