【问题标题】:Lazy loading - what's the best approach?延迟加载 - 最好的方法是什么?
【发布时间】:2009-02-07 16:33:48
【问题描述】:

我见过很多延迟加载的例子——你的选择是什么?

给定一个模型类例如:

public class Person
{
    private IList<Child> _children;
    public IList<Child> Children
    {
        get {
            if (_children == null)
                LoadChildren();
            return _children;
        }
    }
}

Person 类不应该知道它的子类是如何加载的……还是应该知道?当然它应该控制何时填充属性?

您是否有一个将 Person 与其子集合耦合在一起的存储库,或者您是否会使用不同的方法,例如使用 lazyload 类 - 即使这样,我也不希望延迟加载类在我的模型架构中模糊.

如果先请求一个 Person 再请求它的 Children(即在此实例中不延迟加载)或以某种方式延迟加载,您将如何处理性能。

这一切都归结为个人选择吗?

【问题讨论】:

  • 问题 - 这通常是局部优化以牺牲全局优化为代价的一个很好的例子。

标签: c# architecture coding-style lazy-loading methodology


【解决方案1】:

最好的延迟加载是避免它;) 线程安全是您必须立即处理的问题。我不知道有多少次我看到具有 8 个 cpu 内核的生产系统对于使用的每个延迟加载模式运行延迟加载 8 次。至少在服务器启动时,所有服务器内核都倾向于在同一个地方结束。

如果可以的话,让一个 DI 框架为您构建它。如果你不能,我仍然更喜欢显式构造。所以各种AOP魔法根本不跟我切,去类外显式构造吧。不要将它放在 person 类中,只需创建一个以正确方式构造对象的服务。

引入或多或少透明地做这些事情的“魔法”层似乎是个好主意,但我还没有遇到不会产生不可预见和有问题的后果的实现。

【讨论】:

  • 感谢您的宝贵时间,您能否详细说明使用 DI 框架和显式构造?
  • 我认为他的意思是让 Person 类在其自己的初始化期间显式创建子类(大概使用 LoadChildren()),这对于 _children 的状态毫无疑问。还有 Dependency Injection,嗯,这里有很多线程在评论中比我好。
  • 另外——你失去了查询的原子性。通常最有效的总体策略是使用(外部?)子记录的连接发出单个查询,并在一个事务中获取所有内容,而不是将其分散到多个数据库子点击中。
  • @Le Dorfier - 同意性能问题。但是,当一个对象可能有太多的子关系以保证一次加载所有内容时,就会出现一个临界点。在这些情况下你会怎么做?
  • 我也想知道在“孩子关系太多”的情况下该怎么办
【解决方案2】:

您可以使用我在这里谈到的Lazy&lt;T&gt; 类: What is the proper way to inject a data access dependency for lazy loading?

那里还有更详细的博客文章的链接...

【讨论】:

    【解决方案3】:

    我谈到了我用来完成延迟加载的解决方案here

    【讨论】:

    • 感谢您的宝贵时间,这不只是另一个依赖项吗?不过我对这种方法很感兴趣。
    【解决方案4】:

    您可以使用Virtual Proxy 模式以及Observer pattern。这将使您延迟加载,而无需 Person 类明确了解 Children 的加载方式。

    【讨论】:

      【解决方案5】:

      我认为这正是最好由 AOP(例如 PostSharp)处理的问题。将您的延迟加载作为一个方面,然后使用它来装饰您想要延迟加载的任何属性。免责声明:没试过;只是认为它应该起作用。

      【讨论】:

        【解决方案6】:

        我刚刚问了一个相关问题here,但它在不变性和线程安全方面更重要。很多好的答案和cmets。您可能会发现它很有用。

        【讨论】:

          【解决方案7】:

          这是一个使用代理模式实现延迟加载的示例

          将与您的其余模型一起使用的 Person 类。子项被标记为虚拟,因此可以在 PersonProxy 类中被覆盖。

          public class Person {
              public int Id;
              public virtual IList<Child> Children { get; set; }
          }
          

          将与您的其他存储库一起使用的 PersonRepository 类。我在这个类中包含了获取孩子的方法,但如果你愿意,你可以在 ChildRepository 类中使​​用它。

          public class PersonRepository {
              public Person FindById(int id) {
                  // Notice we are creating PersonProxy and not Person
                  Person person = new PersonProxy();
          
                  // Set person properties based on data from the database
          
                  return person;
              }
          
              public IList<Child> GetChildrenForPerson(int personId) {
                  // Return your list of children from the database
              }
          }
          

          与您的存储库一起使用的 PersonProxy 类。这继承自 Person 并将执行延迟加载。您还可以使用布尔值来检查它是否已经加载,而不是检查 Children == null。

          public class PersonProxy : Person {
              private PersonRepository _personRepository = new PersonRepository();
          
              public override IList<Child> Children {
                  get {
                      if (base.Children == null)
                          base.Children = _personRepository.GetChildrenForPerson(this.Id);
          
                      return base.Children;
                  }
                  set { base.Children = value; }
              }
          }
          

          你可以这样使用它

          Person person = new PersonRepository().FindById(1);
          Console.WriteLine(person.Children.Count);
          

          当然,如果您不想直接调用 PersonRepository,您可以让 PersonProxy 接收到 PersonRepository 的接口并通过服务访问它。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2010-09-07
            • 2021-03-27
            • 2012-12-09
            • 1970-01-01
            • 2020-06-28
            • 1970-01-01
            • 1970-01-01
            • 2011-01-12
            相关资源
            最近更新 更多