C# 实体框架-使用依赖对象的长链高效快速加载?

C# 实体框架-使用依赖对象的长链高效快速加载?,c#,asp.net-mvc,performance,entity-framework,linq,C#,Asp.net Mvc,Performance,Entity Framework,Linq,我有一个问题,我想很多专业开发人员都会遇到这个问题。我的工作场所采用了实体框架。我们使用它,并且热爱它。然而,我们似乎遇到了一个非常令人沮丧的限制 假设您有一个这样的对象链 A->B->C->D 我们是专业人员,所以这些对象有很多数据,并且在它们各自的数据库表中有很多数据。似乎EF在加载任何超过对象B的内容时都很糟糕。它生成的SQL查询效率很低,而且不好。这个电话大概是这样的 context.objects.include("bObjectName.cObjectName.dObjectName

我有一个问题,我想很多专业开发人员都会遇到这个问题。我的工作场所采用了实体框架。我们使用它,并且热爱它。然而,我们似乎遇到了一个非常令人沮丧的限制

假设您有一个这样的对象链

A->B->C->D

我们是专业人员,所以这些对象有很多数据,并且在它们各自的数据库表中有很多数据。似乎EF在加载任何超过对象B的内容时都很糟糕。它生成的SQL查询效率很低,而且不好。这个电话大概是这样的

context.objects.include("bObjectName.cObjectName.dObjectName").FirstOrDefault(x => x.PK == somePK);
我们通过使用.Load()命令显式加载超过第二级的对象来解决这个问题。这适用于单个对象。然而,当我们谈论对象集合时,我们开始遇到.Load()的问题

首先,似乎没有一种方法可以在没有virtual关键字的情况下保持集合中对象的代理跟踪。这很有意义,因为它需要覆盖get和set函数。但是,这会启用延迟加载,并且当启用延迟加载时,.Load()不会映射实体。我自己也觉得这有点奇怪。如果删除虚拟关键字,.Load()会自动将加载的对象链接到上下文中的相关对象

这就是我问题的症结所在。我需要代理跟踪,但也需要.Load()为我映射导航属性。如果EF能够生成良好的查询,那么这一切都不会成为问题。我明白为什么它不能,它必须是一个一展即用的东西

因此,要加载第三层对象,我可以在服务层中创建一个加载函数,该函数接受第二层对象的所有主键,然后对它们调用.load()。 有人能解决这个问题吗?EF7或Core 1.0似乎通过以下方式解决了这一问题:

  • 完全删除延迟加载,我们也可以关闭它,但它会破坏许多旧代码
  • 添加了一个新的“thenclude”特性,据说这会大大提高链接include的效率
  • 如果关闭延迟加载是答案,那很好,我只想在浪费大量时间重新开发价值巨大的webapps服务调用之前,用尽所有的选择。 有人有什么想法吗?我愿意尝试任何事情。我们正在使用EF6


    编辑:答案似乎是在上下文级别关闭延迟加载,或者升级到EF7。如果其他任何人设法找到一种解决方案,使您可以在EF6的单个对象上使用强制急切加载进行代理跟踪,我将对此进行更改。

    您对chained.Include()语句的看法绝对正确。Include()语句警告不要在每个.Include()添加外部联接或并集时链接超过3个。我不知道那是怎么回事,但听起来像是游戏规则的改变者

    如果保留虚拟导航属性,但关闭DbContext上的延迟加载

     context.ObjectContext().ContextOptions.LazyLoadingEnabled = false;
    
    然后(只要启用了更改跟踪),您就可以执行以下操作:

    var a = context.aObjectNames.First(x=> x.id == whatever);
    
    context.bObjectNames.Where(x=> x.aId == a.Id).Load()
    

    这应该会填充a.bObjects

    您是否运行SQL Profiler来查看实际的SQL查询?是的,我可以告诉您,只要您链接到第三级,查询就会变得非常低效。因为这些查询是按程序生成的,所以是可以理解的。虽然可以理解,但它不适用于专业级软件。一种解决方案是保存.Load()的结果并手动将其设置为正确的对象。所以在这种情况下,加载所有的C并将它们设置为B,但是这里所有的开发人员都同意,我们不喜欢这样。我们喜欢虚拟删除时的功能,但我们也希望代理跟踪,以便我们可以添加到对象B中的icollection。据我所知,您可以在EF 7 Advantage中添加第三点。它终于实现了批处理查询,这意味着它可以在对SQL的一次调用中绑定多个查询。这应该允许它为依赖实体发出不同的查询,同时仍然导致对SQL的单个调用,而不是以前发出的可怕的单个查询。对于延迟加载的性能问题,由于缺少批处理延迟加载特性,这是一个特定于EF的问题。看见是的,我们用一个控制台应用程序和EF7做了一些测试,它通过“ThenInclude”功能极大地提高了性能和查询生成。如果这是答案,那么这就是答案,我只是想看看是否还有其他人有一个不涉及这么多变化的解决方案。我很感激你的回答。你说的代理追踪到底是什么意思?如果您显示的代码与您期望(或想要)的行为方式不符,则会更加清晰。我无法理解你所怀疑的急切与懒惰加载和跟踪之间的联系。我不知道你说的是什么意思。启用延迟加载时,Load()不映射实体
    Load()
    是一种急切的加载方法,它与延迟加载有什么关系?它将如何映射实体?真的,我们需要代码。我们想保持所有旧代码的延迟加载。看来这是不可能的。延迟加载和更改跟踪紧密结合在一起。这对我来说很奇怪,因为我想不出为什么他们会这样。删除延迟加载需要更改许多旧(读:坏)代码。无论如何都应该这样做,但是时间。。。钱。。。。等等。我将把这标记为答案,因为没有人给EF6给出了更好的答案。您可以在不关闭上下文延迟加载的情况下进行快速加载。导航属性只有在您访问它们时才会加载,因此您可以提前进行快速加载,以避免在访问属性时延迟加载。这实际上取决于您的用例,但是延迟加载并不是天生的坏事,如果您只使用了B或C中的几个属性,那么延迟加载它们而不是拉回整个对象可能是有意义的。显然,如果您迭代t,那么延迟加载不是您的朋友