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.


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;
}

Your problem is not in the Select(y=>y.SomeClass) it self, if you try to remove it from your query and execute your query again, you will get same problem. You cannot query the inherited type as child and you expect from entity framework to take care for everything.

If you look to your database, the table Base has a reference to A which is relation 1-many from A to Base.

you can either get all the Base entities where A.Id = something , by adding a navigational property A in the class Base , and in your DbContext you add DbSet<Base> Bases{get;set;} then your query will look like this

var details = _ctx.Bases.OfType<Base1>()
                        .Include(t=>t.Box)
                        .Include(t=>t.SomeClass)
                        .Where(t=>t.Box.Id ==something);

Other option, to use a DTO, in the below sample I used Anonymous type, but you can create a strongly DTO typed to meet your requirements.

var details  = _ctx.A
                   .Where (t=>t.Id ==something)
                   .Select(a => new {
                        Id = a.Id,
                        // ... other A properites , 
                        Bases = _ctx.Bases.OfType<Base1>().Select(m=> new {
                            Id = m.Id,
                            Name = m.Name,
                            SomeClass = m.SomeClass
                        });
                   }

Hope this will help you

链接地址: http://www.djcxy.com/p/36302.html

上一篇: Python单击:自定义错误消息

下一篇: EF预先获取派生类