【问题标题】:Why is my WPF Treeview bound to LinqToSql classes being a memory hog?为什么我的 WPF Treeview 绑定到 LinqToSql 类会占用内存?
【发布时间】:2009-01-08 11:57:08
【问题描述】:

我有一个 WPF 应用程序,它在内存不足后停止运行...
它基本上是一个显示节点的 TreeView,它们是 Linq To Sql 或生成的类 ICTemplates.Segment 的实例。在 OR 设计器中,大约有 20 个表通过关联间接链接到该类。

<TreeView Grid.Column="0" x:Name="tvwSegments" 
                      ItemsSource="{Binding}" 
                      SelectedItemChanged="OnNewSegmentSelected"/>
<HierarchicalDataTemplate DataType="{x:Type local:Segment}" ItemsSource="{Binding Path=Children}"> 
...

// code behind, set the data context based on user-input (Site, Id)
KeeperOfControls.DataContext = from segment in tblSegments
   where segment.site == iTemplateSite && segment.id == iTemplateSid
   select segment;

我在段类中添加了一个名为 Children 的显式属性,用于查找另一个包含父子记录的表。

public IEnumerable<Segment> Children
{
  get
  {
    System1ConfigDataContext dc = new System1ConfigDataContext();
    return from link in this.ChildLinks
      join segment in dc.Segments on new { Site = link.ChildSite, ID = link.ChildSID } equals new { Site = segment.site, ID = segment.id }
      select segment;
  }
}

剩下的部分是数据绑定加上数据模板,以将每个 Segment 显示为一组 UI 控件。

我很确定孩子正在按响应时间按需加载(当我展开父级时)。当我展开一个包含大约 70 个子节点的节点时,加载子节点需要一段时间(任务管理器显示内存使用为 1000000K!)。如果我用大约 50 个子节点扩展下一个节点,BOOM! OutOfMemoryException

我运行 VS Profiler 进行更深入的挖掘,结果如下

Summary Page Object Lifetimes Allocation

前 3 个是 Action、DeferredSourceFactory.DeferredSource 和 EntitySet(所有 .Net/LINQ 类)。唯一的用户类是 Segment[] 和 Segment 在#9 和 #10。

我想不出要追求的线索.. 可能是什么原因?

【问题讨论】:

    标签: wpf linq-to-sql treeview profiling


    【解决方案1】:

    也许是围绕那个 DataContext 的 using ?

    using(System1ConfigDataContext dc = new System1ConfigDataContext()){
      .... ?
    }
    

    另外,您是否尝试过使用sql profiler?可能会对此事有所启发。

    【讨论】:

    • 感谢 Sirocco。你让我走上了正确的道路......谢谢!有关详细信息,请参阅下面的我的帖子..
    【解决方案2】:

    您是否尝试过为每个元素使用一个全局 DataContext 而不是一个?

    使用自己的查询和结果创建所有 DataContext 可能是导致内存膨胀的原因。

    【讨论】:

    • 更新问题...我为每个查询设置一次 DataContext...不是为每个元素/节点。
    【解决方案3】:

    我不知道确切的解决方案,但 join 中的新语句可能会导致这种情况。因为对于每个关系都可以创建一个新对象(但正如我所提到的,我不知道它是否正确)。

    你可以试试这个吗?

    public IEnumerable<Segment> Children
    {
      get
      {
        System1ConfigDataContext dc = new System1ConfigDataContext();
        return from link in this.ChildLinks
          join segment in dc.Segments on link.ChildSite == segment.site && link.ChildSID == segment.id
          select segment;
      }
    }
    

    【讨论】:

    • 上述 sn-p 无法编译 - 错误 CS0744:预期的上下文关键字“等于”。我已经与 LINQ 中的连接语法发生了冲突。新是 reqd - 见stackoverflow.com/questions/373865/…
    【解决方案4】:

    问题似乎是创建了多个 S1DataContext 对象,正如 Sirocco 所指。 我尝试了 using 语句来强制 Dispose 并使其符合收集条件。但是它导致了我无法理解的 ObjectDisposedException。

    1. 控件来自设置 DockPanel KeeperOfAllControls 的数据上下文的行。
    2. [外部代码](显示在调用堆栈中)
    3. Segment.Children.get(有一个带 dc 的 using 块)
    4. 回到步骤 1 中的行... Linq 查询使用从 S1DataContext 的本地实例检索的 tblSegments

    无论如何,所以我假设存在阻止创建和处置多个 DataContexts 的东西。所以我尝试了 Singleton DataContext
    它有效!

    • TreeView 控件的响应速度明显更快,我尝试的每个节点最多可在 3-4 秒内加载。
    • 我在每次提取/搜索之前放入了一个 GC.Collect(用于验证),现在内存使用量保持在 200,000-300,000K 之间。

    OR 生成的 System.Data.Linq.DataContext 似乎不会消失,除非它被显式处理(吃掉内存)。尝试在我的情况下处理它,并没有成功..即使两个函数都有自己的 using 块(没有 DataContext 的共享实例)。虽然我不喜欢单例,但我正在为开发人员制作一个小型内部工具,因此现在不介意它。我在网上看到的 LinqToSql 示例都没有强制执行 Dispose 调用。

    所以我想问题已经解决了。感谢所有为使这个错误变得浅薄而付出更多努力的人。

    【讨论】:

    • ChildLinks 可能是 LazyLoaded - 这就是你得到 ObjectDisposedException :) 的原因。最好的事情可能是为业务事务实现提供数据上下文。类似于网络上每个会话的 DataContext。
    • 所以如果我尝试强制执行以击败延迟执行(在返回之前将结果取出到列表中),我应该不会看到这个异常?
    • 是的,或者您可以(使用适当的 DataContext - 在 Children 属性中)在上下文中设置 DataLoadOptions 以在加载段时加载 ChildLinks。这会触发额外的连接并使结果翻倍——我不确定 L2S 是否知道如何处理。
    • 当我说双倍 - 我的意思是乘:)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-12
    • 2017-07-09
    • 2011-01-31
    • 2021-07-13
    • 1970-01-01
    相关资源
    最近更新 更多