简体   繁体   English

如何在实体框架中一次显式加载多个模型的关系?

[英]How to explicitly load relations for multiple models at once in Entity Framework?

I have a model similar to the following: 我有一个类似于以下的模型:

class Parent {
    public int Id { get; set; }
    public string Name { get;set; }
    public ICollection<Child> Children { get; set; }
    public GrandChildren SpecialGrandChild { 
        get {
            return Children.SelectMany(c => c.Children).Where(...).Single();
        }
    }
}

class Child {
    public int Id { get; set; }
    public int ParentId { get; set; }
    public Parent Parent { get; set; }
    public ICollection<GrandChild> Children { get; set; }
}

class GrandChild {
    public int Id { get; set; }
    public string Name { get;set; }
    public int ParentId { get; set; }
    public Child Parent { get; set; }
}

I also have a fairly complex query involving all three tables. 我也有一个涉及所有三个表的相当复杂的查询。 From that query I want to extract all the Parent objects, and I will be displaying a property of the SpecialGrandChild for each one. 从该查询中,我想提取所有Parent对象,并且我将为每个对象显示SpecialGrandChild的属性。

The problem is that if I do: 问题是,如果我这样做:

query.Include(p => p.Children.Select(c => c.Children));

EF will generate an ungodly sql query, and take a ton of time to build the query (on some cases over 10 seconds!). EF会生成一个不友好的sql查询,并花费大量时间来构建查询(在某些情况下会超过10秒!)。 The query is cached so further calls are much faster. 查询被缓存,因此进一步的调用要快得多。 If I drop the Include call, I do not get such a bad first-call performance, but of course I get a worse performance as I will be doing M*N+1 queries (for each Parent, fetch the Children, and for each Child fetch the GrandChildren). 如果放弃Include调用,我不会获得如此差的首次调用性能,但是当然,我会得到更差的性能,因为我将执行M*N+1查询(对于每个父级,获取子级以及每个孩子拿来GrandChildren)。

So the question is: can I explicitly load all the Children and GrandChildren for all the loaded Parents in a single call? 所以问题是:我可以在一次调用中为所有已加载的“父母”显式加载所有“子级”和“孙子级”吗? If so, how can I do so? 如果是这样,我该怎么办?

I tried querying all the Childs for the currently loaded Parents as follows: 我尝试查询所有Childs以获取当前加载的Parents,如下所示:

var ids = parents.Select(p => p.Id);
(from c in Childs where ids.Contains(c.ParentId) select c).Include("Children").Load();

But that call does not tell EF that all the associated Childs are loaded so it still goes to the DB when I access the association properties. 但是,该调用不会告诉EF所有关联的子级都已加载,因此当我访问关联属性时,它仍将转到DB。

Load your data in two steps: 分两步加载数据:

var dbParent = ...; // query all Parent's
var dbChild = ...; // query all Child's

var parents = dbParent.Include(p => p.Children).ToList();
dbChild.Include(p => p.Children).toList();

That should make parents have a list of all parents, and because of tracking, each parent will have each of its children. 这应该使父母拥有所有父母的列表,并且由于跟踪,每个父母将拥有其每个孩子。

If you have to apply a filter condition on parent, you should make it on children too. 如果必须对父母应用过滤条件,则也应对孩子应用过滤条件。

Since I don't know enough about your parent variable and a context your parent objects are probably attached on (or maybe not) I can't give you a precise answer. 由于我对您的父变量和上下文不了解,您的父对象可能附加在(或可能不附加)了,所以我无法给您一个确切的答案。 But what you're looking for should look like this where ctx is an instance of your context and Parents a DbSet Property: 但是您要查找的内容应如下所示,其中ctx是上下文的实例,而Parents是DbSet属性:

 IQueryable<Parent> query = ctx.Parents.Include("Children.Children");
 List<Parent> myTree = query.toList();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM