是否应该使用实体框架4.1和MVC3启用或禁用动态代理?


68

有人可以提供一些建议或指出一些博客/文章来帮助做出这一决定吗?代理对我来说似乎很陌生,我不愿意使用它们。我喜欢通过在模型中使用虚拟属性来控制延迟加载的功能,但这几乎是我可以看到的所有好处。我的应用程序是一个简单的MVC Web应用程序,当实体经历更改状态时,我不需要为上下文绑定任何挂钩。

无论如何,这是我目前优缺点非常有限的清单,请让我知道我是否在这方面不满意。

优点

  • 在“保存”或“更新”上,我可以无缝地应用“更改”
  • 延迟加载配置非常容易。

缺点

  • 对于我的实体,以前从未使用过代理,这是一种方法上的变化,对我自己和团队成员而言似乎不舒服。
  • 难以调试。
  • 如果我要序列化/反序列化,则需要额外的代码
  • 在“保存”或“更新”上,代理必须与从上下文中检索到的对象相同。

7
+1-序列化/反序列化问题很痛苦!
StuartLC 2011年

Answers:


103

如果您谈论EF中的动态代理,则有两种不同的类型可以区分:

  • 延迟加载的代理
  • 变更跟踪代理

通常,变更跟踪代理还可以充当延迟加载的代理。反之则不成立。这是因为对变更跟踪代理的要求更高,尤其是所有属性(标量属性)也必须为virtual。对于延迟加载,导航属性为就足够了virtual

更改跟踪代理始终还可以利用延迟加载这一事实是DbContext具有此配置标志的主要原因:

DbContext.Configuration.LazyLoadingEnabled

默认情况下,此标志为true。将其设置为false禁用延迟加载,即使创建了代理也是如此。如果您正在使用变更跟踪代理,但又不想将这些代理用于延迟加载,则这一点尤其重要。

选项 ...

DbContext.Configuration.ProxyCreationEnabled

...完全禁用代理创建-以及更改跟踪和延迟加载。

仅当您的实体类满足创建更改跟踪或延迟加载代理的要求时,这两个标志才完全有意义。

现在,您知道了动态延迟加载代理的目的。那么,为什么要使用动态变更跟踪代理呢?

实际上,我知道的唯一原因是性能。但这是一个非常有力的理由。将基于快照的更改跟踪与基于代理的更改跟踪进行比较,性能差异是巨大的-从我的测量中可以得出50到100的因子是现实的(取材于一种方法,该方法需要大约一小时的10000个实体,并且具有基于快照的更改跟踪和30至60秒将所有属性设为虚拟以启用更改跟踪代理后)。如果您有一些应用程序可以处理和更改许多(例如超过1000个)实体,那么这将成为一个重要因素。在Web应用程序中,您可能只对Web请求中的单个实体执行“创建/更改/删除”操作,此区别无关紧要。

在几乎所有情况下,如果您不想使用延迟加载代理,都可以利用紧急加载或显式加载来实现相同的目标。基于代理的延迟加载或基于非代理的显式加载的性能是相同的,因为加载导航属性时会发生基本上相同的查询-第一种情况是代理执行查询,第二种情况是您的手写代码。因此,您可以活着而无需延迟加载代理,而不会损失太多。

但是,如果您想要合理的性能来处理很多很多实体,除了使用EntityObjectEF 4.0中的派生实体(不是EF 4.1中的一个选项,因为使用时禁止使用DbContext)或完全不使用Entity Framework,没有其他方法可以替代更改跟踪代理。

编辑(2012年5月)

同时,我了解到在某些情况下,与基于快照的跟踪相比,更改跟踪代理的性能不会更快甚至更差。

由于使用更改跟踪代理时存在这些复杂性,因此首选方法是默认使用基于快照的更改跟踪,并且仅在要求高性能且事实证明它们比基于快照快的情况下,谨慎使用代理(在进行一些测试之后)变更追踪。


1
@Slauma-我不知道您可以即时更改那些配置,并且认为仅在进行域定义时可用。通过使用if (DisableProxy) { context.Configuration.ProxyCreationEnabled = false; context.Configuration.LazyLoadingEnabled = false; }I,我可以允许这些动态代理进行序列化而不会引起循环引用错误。谢谢!+1
特拉维斯J

15

对于使用Entity Framework 5的任何人,请务必查看“性能注意事项”一文。Sections 5 NoTracking Queries8 Loading Related Entities提供您做出明智决定所需的信息。干杯。


2

我建议不要使用代理。动态代理的创建会中断或增加依赖于运行时类型检查的组件的复杂性。

例如,自动映射器将在运行时抛出类型不匹配/意外类型错误,因为您的实体将在运行时动态生成代理类型,而不是您在配置自动映射时传入的类型。


1
Automapper正是我开始研究此问题的原因,也是动态代理潜在转变的缺点。
Jim Wolff

我开始讨厌动态代理的整个概念。首先,与代理相关的问题仅在运行时显示。通常,只有在满足非显而易见的条件(通常会被测试人员打滑)的生产中才能使用。其次,这是一个泄漏性的抽象,因为调用代码通常必须了解代理的存在和特殊需求。从那以后,我放弃了整个概念,将其关闭,并更新了我的设计-从未回头。动态代理应该在头部的后部射击。
CShark

3
朋友不允许朋友使用动态代理。
CShark

我最终通常将其关闭,以便从数据库中获取“只读”信息,并在写入时将其保持打开状态(以进行更改跟踪),但是我测试了更改跟踪是否可以在没有代理的情况下工作,并且为简单起见,它可能会全部禁用它。在特殊的创建方案中,不要让其他开发人员失望。
Jim Wolff

您似乎正在朝正确的方向前进。我以类似的方式将CQRS模式用于数据访问,并且效果很好。
CShark '16

0

尽管动态代理具有一些不错的功能,但实际上,它们可以创建许多奇怪且晦涩的错误。

例如,在我的一个类中(它正在执行批处理过程)保留一个实体的私有变量,而我正在遍历数百万条记录,进行处理并将它们分批插入,重新创建每个n条记录的数据上下文。清理内存。尽管我从不使用私有变量,但即使我仅设置引用ID,EF仍将其链接到我的新对象(通过导航属性提供了引用)。

这导致所有对象在进程运行的整个过程中都保留在内存中。我必须使用AsNoTracking并禁用代理才能使进程按预期方式工作,并使内存和性能恢复到正常水平。请记住,代理也引用创建它们的上下文,这可以将巨大的实体图保存在内存中,几乎不可能对其进行调试

因此,我认为您应该全局禁用代理,并以小的且包含代码的段启用它们。当您有大型团队进行编码时,调试此类问题非常危险且不可能进行调试。

更改跟踪很好,它可以证明某些地方的用法合理。除非您知道自己在做什么,否则延迟加载对于性能和序列化可能是一个巨大的问题。我更喜欢始终渴望或明确加载。


0

使用Automapper 4.2.1。新版本没有DynamicMap

var parents = parentsRepo.GetAll().ToList();
Mapper.CreateMap<Parent,ParentDto>();
var parentsDto = Mapper.DynamicMap<List<ParentDto>>(parents);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.