首页 文章

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

提问于
浏览
66

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

无论如何,这是我现在非常有限的利弊列表,让我知道如果我没有任何这个 .

Pros

  • 在'Save'或'Update',我与'Apply'更改无缝

  • 延迟加载配置非常简单 .

Cons

  • 之前没有为我的实体使用代理,这是一种方法的改变,对我自己和团队成员来说似乎不舒服 .

  • 调试很尴尬 .

  • 如果我想序列化/反序列化,需要额外的代码

  • 在'Save'或'Update'上,代理必须是从上下文中检索的同一对象 .

5 回答

  • 101

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

    • 延迟加载的代理

    • 变更跟踪代理

    通常,更改跟踪代理也可以作为延迟加载的代理 . 反之则不然 . 这是因为更改跟踪代理的要求更高,尤其是所有属性 - 也就是标量属性 - 必须是 virtual . 对于延迟加载,导航属性为 virtual 就足够了 .

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

    DbContext.Configuration.LazyLoadingEnabled
    

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

    选项 ...

    DbContext.Configuration.ProxyCreationEnabled
    

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

    如果您的实体类满足创建更改跟踪或延迟加载代理的要求,则两个标记都只有一个含义 .

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

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

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

    但是如果你想要合理的性能来处理许多实体,除了在EF 4.0中使用 EntityObject 派生实体(不是EF 4.1中的选项,因为它在你使用 DbContext 时被禁止)或不使用实体框架时,除了更改跟踪代理之外别无选择一点都不

    Edit (May 2012)

    与此同时,我了解到,与基于快照的跟踪相比,有些情况下change tracking proxies的性能不会更快甚至更差 .

    由于使用更改跟踪代理时出现这些复杂情况,首选方法是默认情况下使用基于快照的更改跟踪,并且仅在需要高性能并且证明比基于快照的速度更快的情况下仔细使用代理(在执行某些测试之后)改变跟踪 .

  • 15

    对于使用Entity Framework 5的任何人,请务必看看Performance Considerations文章 . Sections 5 NoTracking Queries8 Loading Related Entities 提供了做出明智决定所需的信息 . 干杯 .

  • 2

    我建议不要使用代理 . 动态代理创建会破坏或创建依赖于运行时类型检查的组件的复杂性 .

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

  • 0

    虽然动态代理具有一些很好的功能,但实际上它们可以创建许多奇怪且模糊的错误 .

    例如,在我的一个类中保存了一个实体的私有变量(它正在实现一个批处理过程),我循环遍历几百万个记录,批量处理和插入它们,每个n记录重新创建数据上下文清理记忆 . 虽然我从来没有使用私有变量,但EF将它链接到我的新对象(通过导航属性有一个引用),即使我只设置了引用ID .

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

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

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

  • 0

    使用Automapper 4.2.1 . 新版本没有DynamicMap

    var parents = parentsRepo.GetAll().ToList();
    Mapper.CreateMap<Parent,ParentDto>();
    var parentsDto = Mapper.DynamicMap<List<ParentDto>>(parents);
    

相关问题