【问题标题】:EF Eager fetching derived classEF Eager 获取派生类
【发布时间】:2016-09-20 19:08:11
【问题描述】:

我正在使用 EF6 并尝试获取对象的整个结构。问题是我正在使用继承。

假设我有这个课程。

DbContext

DbSet<A> A { get; set; }

示例类

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

为什么它不适用于以下内容?

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

新示例

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

编辑:添加了一个似乎可行的新示例。

【问题讨论】:

  • 你试过使用Include("SomeOtherClass")吗?
  • Someclass 是另一个实体。我还没有尝试过 SomeOtherClass,因为 SomeClass 还不起作用
  • 这说明你试过Include("SomeClass")还是不行?
  • 是的,我收到了我在帖子中写的错误:(
  • 我认为您遇到了 TPH(以及一般的 EF 继承)的限制之一。

标签: c# entity-framework entity-framework-6


【解决方案1】:

简而言之,开箱即用是不可能的。

我可以建议的唯一解决方法是实现主查询结果,然后使用与主查询相同的过滤器执行几个 OfType 查询和必要的 Includes,并依赖 EF 导航属性修复。

它需要Base 类中的反向导航属性:

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

然后你可以使用这样的东西:

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

同样的想法可以在不使用反向导航属性的情况下使用,但使用返回的基本 ID 作为过滤器:

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

【讨论】:

  • 谢谢。 :) 我已经尝试了您的第一个示例,但没有反向导航属性(Base 中的 A ref)并且 Base1 和 Base3 上都没有 Where ?我已经更新了我的帖子。我的例子会和你的完全一样吗?或者有什么区别?
  • @Henrik 不错!这实际上是使用相同基本思想的第三种方式。这是我第一种方法的变体,虽然这 3 种方法都有效,但这似乎是这种情况下的最佳选择。
【解决方案2】:

您的问题不在于 Select(y=&gt;y.SomeClass) 它本身,如果您尝试将其从查询中删除并再次执行查询,您将遇到同样的问题。您不能将继承的类型查询为子类型,并且您希望实体框架能够处理所有事情。

如果您查看您的数据库,表 Base 具有对 A 的引用,这是从 A 到 Base 的 1-many 关系。

您可以通过在Base 类中添加导航属性A 来获取所有Base 实体A.Id = something,然后在您的DbContext 中添加DbSet&lt;Base&gt; Bases{get;set;},然后您的查询将如下所示

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

其他选项,使用 DTO,在下面的示例中,我使用了 Anonymous 类型,但您可以创建一个强类型的 DTO 以满足您的要求。

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

希望对你有帮助

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-06-10
    • 1970-01-01
    • 2015-03-30
    • 2011-01-11
    • 2011-10-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多