68

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

无论如何,这是我现在非常有限的利弊清单,如果我对这些有任何误解,请告诉我。

优点

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

缺点

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

5 回答 5

103

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

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

通常更改跟踪代理也可以作为延迟加载的代理。反过来是不正确的。这是因为对更改跟踪代理的要求更高,尤其是所有属性——也是标量属性——必须是virtual. 对于延迟加载,导航属性为virtual.

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

DbContext.Configuration.LazyLoadingEnabled

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

选项 ...

DbContext.Configuration.ProxyCreationEnabled

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

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

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

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

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

EntityObject但是,如果您希望以合理的性能来处理很多很多实体,则除了在 EF 4.0 中使用派生实体(在 EF 4.1 中不是一个选项,因为在使用时禁止使用DbContext)或根本不使用 Entity Framework之外,没有其他方法可以更改跟踪代理.

编辑(2012 年 5 月)

与此同时,我了解到,与基于快照的跟踪相比,更改跟踪代理在性能上并没有更快甚至更差的情况。

由于使用更改跟踪代理时的这些复杂性,首选方法是默认使用基于快照的更改跟踪,并且仅在需要高性能且证明比基于快照更快的情况下谨慎使用代理(经过一些测试)变化跟踪。

于 2011-08-18T18:44:04.837 回答
15

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

于 2012-10-11T23:09:58.100 回答
2

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

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

于 2015-09-24T14:25:04.040 回答
0

尽管动态代理有一些不错的功能,但实际上它们会产生很多奇怪和晦涩的错误。

例如,在我的一个类中保留了一个实体的私有变量(它正在实现批处理),我正在循环数百万条记录,批量处理和插入它们,每 n 条记录重新创建数据上下文清理内存。尽管我从未使用过私有变量,但 EF 将它链接到我的新对象(通过导航属性进行了引用),即使我只设置了引用 ID。

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

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

更改跟踪很好,它可能证明在某些地方使用是合理的。除非您知道自己在做什么,否则延迟加载在性能和序列化方面可能是一个巨大的问题。我总是更喜欢急切或显式加载。

于 2016-06-08T13:43:25.960 回答
0

使用 Automapper 4.2.1。新版本没有 DynamicMap

var parents = parentsRepo.GetAll().ToList();
Mapper.CreateMap<Parent,ParentDto>();
var parentsDto = Mapper.DynamicMap<List<ParentDto>>(parents);
于 2017-07-06T13:17:22.407 回答