我应该使用实体框架 4.1 和 MVC3 启用或禁用动态代理吗?
Posted
技术标签:
【中文标题】我应该使用实体框架 4.1 和 MVC3 启用或禁用动态代理吗?【英文标题】:Should I enable or disable dynamic proxies with entity framework 4.1 and MVC3? 【发布时间】:2011-10-29 23:31:04 【问题描述】:有人可以提供一些建议或指出一些有助于做出此决定的博客/文章吗?代理对我来说似乎很陌生,我对使用它们犹豫不决。我喜欢通过在我的模型中使用虚拟属性来控制延迟加载的能力,但这几乎是我能看到的所有好处。我的应用程序是一个简单的 MVC Web 应用程序,当实体经历更改状态时,我不需要将任何挂钩连接到上下文中。
无论如何,这是我目前非常有限的利弊列表,如果我对其中任何一个不满意,请告诉我。
优点
在“保存”或“更新”时,我可以无缝地使用“应用”更改 延迟加载配置非常简单。缺点
我的实体以前从未使用过代理,这是在 对我自己和其他团队来说似乎不舒服的方法 成员。 难以调试。 如果我想序列化/反序列化,需要额外的代码 在“保存”或“更新”时,代理必须与从上下文中检索到的对象相同。【问题讨论】:
+1 - 序列化/反序列化问题很痛苦! 【参考方案1】:如果您谈论 EF 中的动态代理,则需要区分两种不同的类型:
延迟加载代理 变更跟踪代理通常更改跟踪代理也可以作为延迟加载的代理。反过来是不正确的。这是因为更改跟踪代理的要求更高,尤其是 所有 属性 - 也是标量属性 - 必须为 virtual
。对于延迟加载,导航属性为 virtual
就足够了。
更改跟踪代理始终允许利用延迟加载这一事实是 DbContext 具有此配置标志的主要原因:
DbContext.Configuration.LazyLoadingEnabled
此标志默认为真。将其设置为 false
会禁用延迟加载,即使已创建代理也是如此。如果您正在使用更改跟踪代理但又不想将这些代理用于延迟加载,这一点尤其重要。
选项...
DbContext.Configuration.ProxyCreationEnabled
... 完全禁用代理创建 - 也用于更改跟踪和延迟加载。
只有当您的实体类满足创建更改跟踪或延迟加载代理的要求时,这两个标志才有意义。
现在,您知道动态延迟加载代理的用途了。那么,为什么要使用动态变化跟踪代理呢?
实际上,我知道的唯一原因是性能。但这是一个非常有力的理由。将基于快照的更改跟踪与基于代理的更改跟踪进行比较,性能差异是巨大的 - 从我的测量结果来看,50 到 100 的因子是现实的(取自一种方法,对于 10000 个具有基于快照的更改跟踪和 30 到 60 秒的实体,大约需要一个小时在将所有属性设为虚拟以启用更改跟踪代理之后)。如果您有一些应用程序可以处理和更改许多(比如超过 1000 个)实体,这将成为一个重要因素。在 Web 应用程序中,您可能只对 Web 请求中的单个实体执行 Create/Change/Delete 操作,这种差异并不重要。
在几乎所有情况下,如果您不想使用延迟加载代理,您都可以利用急切或显式加载来实现相同的目标。基于代理的延迟加载或基于非代理的显式加载的性能是相同的,因为在加载导航属性时基本上会发生相同的查询 - 在第一种情况下,代理执行查询,在第二种情况下您的手写代码。因此,您可以不用延迟加载代理而不会损失太多。
但是,如果您希望以合理的性能来处理很多很多实体,则除了在 EF 4.0 中使用 EntityObject
派生实体之外别无选择(在 EF 4.1 中不是一个选项,因为在您使用 @987654328 时它是被禁止的@) 或根本不使用实体框架。
编辑(2012 年 5 月)
与此同时,我了解到,与基于快照的跟踪相比,change tracking proxies 的性能并没有更快甚至更差。
由于使用变更跟踪代理时的这些复杂性,首选方法是默认使用基于快照的变更跟踪,并且仅在需要高性能且证明更快的情况下谨慎使用代理(经过一些测试)而不是基于快照的更改跟踪。
【讨论】:
@Slauma - 我不知道您可以即时更改这些配置,并认为它仅在进行域定义时可用。使用if (DisableProxy) context.Configuration.ProxyCreationEnabled = false; context.Configuration.LazyLoadingEnabled = false;
,我能够允许这些动态代理可用于序列化,而不会导致循环引用错误。谢谢! +1【参考方案2】:
对于使用 Entity Framework 5 的任何人,请务必查看 Performance Considerations 文章。 Sections 5 NoTracking Queries
和 8 Loading Related Entities
提供您做出明智决定所需的信息。干杯。
【讨论】:
【参考方案3】:我建议不要使用代理。动态代理的创建会破坏依赖于运行时类型检查的组件或造成复杂性。
例如,Automapper 将在运行时引发类型不匹配/意外类型错误,因为您的实体将在运行时动态生成代理类型,而不是您在配置自动映射时传入的类型。
【讨论】:
Automapper 正是我开始研究这个问题的原因,以及可能转向动态代理的不利之处。 我越来越讨厌动态代理的整个概念。首先,与代理相关的问题仅在运行时出现。通常,只有在满足非显而易见的条件时才在生产中(这通常会被测试人员忽略)。其次,这是一个有漏洞的抽象,因为调用代码通常必须知道代理的存在和特殊需求。从那以后,我放弃了整个概念,将其关闭,并更新了我的设计——并且从未回头。动态代理应该在后脑勺拍摄。 朋友不要让朋友使用动态代理。 我最终通常将其关闭以从数据库中获取“只读”,并在写入时保持打开(用于更改跟踪),但我测试了更改跟踪是否在没有代理的情况下工作,并且确实可以禁用它为了简单起见。以免在特殊的仅创建场景中绊倒其他开发人员。 您似乎正朝着正确的方向前进。我以类似的方式使用 CQRS 模式进行数据访问,效果很好。【参考方案4】:虽然动态代理有一些不错的功能,但实际上它们会产生很多奇怪且晦涩的错误。
例如,在我的一个类中保留了一个实体的私有变量(它正在实现一个批处理),我正在循环数百万条记录,批量处理和插入它们,每隔 n 重新创建数据上下文- 清理内存的记录。尽管我从未使用过私有变量,但 EF 将它链接到我的新对象(通过导航属性进行了引用),即使我只设置了引用 ID。
这导致所有对象在整个进程运行期间都保留在内存中。我必须使用 AsNoTracking 并禁用代理,以便进程按预期工作,内存和性能恢复到正常水平。请记住,代理还引用创建它们的上下文,这可以在内存中保存大量实体图,几乎不可能对其进行调试
因此,我认为您应该全局禁用代理,并在小而包含的代码中启用它们。特别是当您有大型团队编码时,调试此类问题是非常危险且不可能的。
更改跟踪很好,它可能证明在某些地方使用是合理的。 除非您知道自己在做什么,否则延迟加载在性能和序列化方面可能是一个巨大的问题。我总是更喜欢急切或显式加载。
【讨论】:
【参考方案5】:使用 Automapper 4.2.1。新版本没有DynamicMap
var parents = parentsRepo.GetAll().ToList();
Mapper.CreateMap<Parent,ParentDto>();
var parentsDto = Mapper.DynamicMap<List<ParentDto>>(parents);
【讨论】:
以上是关于我应该使用实体框架 4.1 和 MVC3 启用或禁用动态代理吗?的主要内容,如果未能解决你的问题,请参考以下文章
具有最佳实践的示例 N 层 ASP.NET MVC3 应用程序(使用 EF 4.1)
我应该如何在 MVC3 中使用 Code First Entity Framework (4.1) 声明外键关系?