Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EF Eager fetching derived class

I´m using EF6 and trying to eager fetch the whole structure of an object. The problem is that i´m using inheritance.

Let´s say that i have this classes.

DbContext

DbSet<A> A { get; set; }

Example classes

public class A
{
    public string Id { get; set; }
    public IList<Base> Bases { get; set; }
}

public abstract class Base
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public abstract class Base1 : Base
{
    public SomeClass SomeClass { get; set; }
}

public class Base2 : Base1
{

}

public class Base3 : Base1
{
    public SomeOtherClass SomeOtherClass { get; set; }
}

The error i get is:

The Include path expression must refer to a navigation property defined on the type. 
Use dotted paths for reference navigation properties and the Select operator for collection navigation properties.

Why doesn´t it work with the following ?

    public IEnumerable<A> GetAll(string id)
    {

        return _ctx.A
               .Include(x => x.Bases.OfType<Base1>().Select(y=>y.SomeClass))
               .Where(x => x.Id.Equals(id)).ToList();
    }

New example

public IEnumerable<A> GetAll(string id)
{

   var lists = _dbContext.A.Where(x => x.Id == id);
   lists.SelectMany(a => a.Bases).OfType<Base1>().Include(e=>e.SomeClass).Load();
   lists.SelectMany(b => b.Bases).OfType<Base3>().Include(e => e.SomeOtherClass).Load();

   return lists;
}

EDIT: Added a new example that seems to work.

like image 695
Henrik Avatar asked Sep 20 '16 19:09

Henrik


People also ask

How do I enable eager loading in Entity Framework?

Eager loading is the process whereby a query for one type of entity also loads related entities as part of the query. Eager loading is achieved by use of the Include method. For example, the queries below will load blogs and all the posts related to each blog. Include is an extension method in the System.

What is eager loading in EF core?

Eager loading means that the related data is loaded from the database as part of the initial query. Explicit loading means that the related data is explicitly loaded from the database at a later time.

How do I turn off lazy loading in Entity Framework?

We can disable lazy loading for a particular entity or a context. To turn off lazy loading for a particular property, do not make it virtual. To turn off lazy loading for all entities in the context, set its configuration property to false.

How do I turn on lazy loading in EF core?

Lazy loading with proxiesAddDbContext<BloggingContext>( b => b. UseLazyLoadingProxies() . UseSqlServer(myConnectionString)); EF Core will then enable lazy loading for any navigation property that can be overridden--that is, it must be virtual and on a class that can be inherited from.


1 Answers

Shortly, it's not possible out of the box.

The only workaround I can suggest is to materialize the master query result, then execute several OfType queries with the necessary Includes using the same filter as the master query, and rely on EF navigation property fixup.

It requires an inverse navigation property in the Base class:

public abstract class Base
{
   // ...
   public A A { get; set; }
}

Then you can use something like this:

public IEnumerable<A> GetAll(string id)
{
    var a = _ctx.A.Where(x => x.Id == id).ToList();
    _ctx.Base.OfType<Base1>().Include(e => e.SomeClass).Where(e => e.A.Id == id).Load();
    _ctx.Base.OfType<Base3>().Include(e => e.SomeOtherClass).Where(e => e.A.Id == id).Load();
    return a;
}

The same idea can be used w/o inverse navigation property but with using the returned base Ids as filter:

public IEnumerable<A> GetAll(string id)
{
    var a = _ctx.A.Include(e => e.Bases)
        .Where(x => x.Id == id).ToList();

    var baseIds = a.SelectMany(e => e.Bases.OfType<ModelA.Base1>().Select(b => b.Id));
    db.Base.OfType<Base1>().Include(e => e.SomeClass)
        .Where(e => baseIds.Contains(e.Id)).Load();

    baseIds = a.SelectMany(e => e.Bases.OfType<Base3>().Select(b => b.Id));
    db.Base.OfType<Base3>().Include(e => e.SomeOtherClass)
        .Where(e => baseIds.Contains(e.Id)).Load();

    return a;
}
like image 62
Ivan Stoev Avatar answered Nov 02 '22 07:11

Ivan Stoev