【问题标题】:SPWeb.Site, should you call Dispose() on it?SPWeb.Site,你应该调用 Dispose() 吗?
【发布时间】:2010-11-17 07:05:04
【问题描述】:

更新于 06/08/2009 15:52简答 原始问题:

我找不到任何关于 SPWeb.Site 处置指导的参考资料。我浏览了一些关于处置 SharePoint 对象的更流行的最佳实践文档:

很遗憾,这些指南均未提及 SPWeb.Site。为了提供一些上下文,我正在编写一个公共扩展 API,它接受 SPWeb 作为方法的参数,即:

public static void GetWebPartFromCatalog(this SPWeb web, string webPartName)
{
     ......

     SPSite site = web.Site;
     ......

     **OR** ??

     using (SPSite site = web.Site)
     {
         ....
     }
}

我查看了 SPWeb 反射器中的 Close() 方法,该方法由 SPWeb.Dispose() 调用,其中没有任何内容表明实际的 SPSite 成员字段已被处理。

更新:06/08/2009 13:47

Alex's建议

“将其放入一个运行 100 次的循环中,并使用在 WSS v3 和 MOSS 2007 中排除 SPSite/SPWeb 泄漏中描述的 SPRequestStackTrace 注册表项来检查您的测试代码是否是问题的根源。”

我运行了 webpart 中包含的以下代码:

 for (int i = 0; i < 100; i++)
 {
     using (SPWeb web = SPContext.Current.Site.OpenWeb(""))
     {
            SPSite site = web.Site;
            Debug.WriteLine(site.Url);
     }
 }

SharePoint 日志中未显示任何内容。

虽然我不愿从这个幼稚的实验中得出任何真正的结论,但它表明没有有必要处置 SPWeb.Site。如果能从对此主题更了解的人那里得到具体的回答,那就太好了。

更新:06/08/2009 14:52 在 Greg 的评论的提示下,我计算出了 m_Site 的分配,看起来它最终总是通过内部构造函数传递给 SPWeb。例如。 SPWeb.OpenWeb 将 this 传递给 new SPWeb()。所以我更确定 SPWeb.Site 应该被处理掉,如果被处理确实会导致问题。

【问题讨论】:

  • 看这个,不清楚。我问了与 Roger Lamb 帖子评论相同的问题,但没有得到回复。
  • 在我看来,这只是一个意见,不,你不应该处理那个对象。为什么?嗯,它是通过另一个对象的属性返回的。如果其他对象是一次性的,您应该处置 那个,并让它处理自己的资源。您从属性中读取的任何内容都不应由您自己的代码处理,在我看来,这将构成该框架中的错误。
  • @lasse-v-karlsen 我 100% 同意你的观点,但是 SharePoint API 中存在许多怪癖,你真的需要了解这些怪癖,否则会有内存泄漏的风险。因此,虽然它可能是 API 中的一个错误,但我想了解它是否存在 :)
  • Keith Dahlby 尝试为 SPList.ParentWeb 解决这个问题的过程:solutionizing.net/2008/08/10/is-splistparentweb-a-leak
  • 很好的讨论和实验在这里 - 抱歉我错过了! :)

标签: sharepoint dispose


【解决方案1】:

Kirk's answer 是正确的。在创建 SPWeb 之前,您必须对 SPSite 有一些句柄,这与调用 SPWeb.Site 时将拥有的 SPSite 实例相同。

让我们考虑一下它的含义 - 如果您不控制 SPSite 的创建,但它的子网站之一是从外部代码交给您的,并且您在控制权返回给调用代码时处置该网站,您已经处置了一个他们可能无法完成的站点!设身处地为调用代码着想:您将 SPWeb 传递给一个方法,当该方法完成时,您正在使用的 SPSite 已关闭。清理他们分配的资源始终是实例化者的责任。在这种情况下不要丢弃 SPSite。

【讨论】:

  • 根据同样的理论,SPLimitedWebPartManager 也应该清理,但它没有! blogs.msdn.com/rogerla/archive/2008/02/12/… API 中存在这种或其他不一致的原因吗?
  • @Alex 我不明白这是怎么回事。在您链接到的示例中,SPSite 和 SPWeb 仍然需要显式清理,以及由 SPLimitedWebPartManager 实例化的 other 网络。
  • Rex 说得很好。您应该始终将 SPWeb 视为“属于”一个 SPSite,而不是相反。特别是因为过早地处置 SPSite 将首先在从它创建的所有 SPWeb 上调用 Dispose()。因此,我总是*假设一个 SPSite 将由创建它的代码处理。 (* wiki 文章记录了所有已知的 SPSite 清理是您的工作的案例。)
  • 想我现在明白了。如果我创建 SPLimitedWebPartManager,那么我的工作就是清理它及其关联的 Web 属性。我不明白的是 API 并不是为我设计的。我猜还有一个问题……
  • 我怀疑 LWPM 会故意泄露 SPWeb。它可能只是被忽略并且永远不会被修复,因为这可能会破坏清理泄漏的代码。
【解决方案2】:

只是想不通(有时很危险)......

似乎没有 SPSite 就无法真正拥有 SPWeb。因此,如果您在没有通过 SPSite 的情况下获得了 SPWeb(通过新的 SPSite 或提供给您的 SPSite),那么您可能无需担心处置 SPSite。

不过,这只是推测。好问题!

【讨论】:

  • 问题是因为这是 SPWeb 的扩展方法,调用者很可能已经创建了 SPSite 对象。但是我没有对 SPSite 对象的引用,因此需要使用 SPWeb.Site。这导致了这个问题,我需要自己清理吗:) 我可以要求调用者包含 SPSite(将其添加到参数列表中),但我不想增加该方法的参数。
【解决方案3】:

这不清楚。 Stefan's blog 声明 “您需要确保只处置您的代码拥有的 SPSite 和 SPWeb 对象”。然后是来自 Michael Washam(Microsoft)的 this thread 指出这种模式确实泄漏了。

除非您可以找到其他参考资料或其他人知道,否则为什么不在您的开发服务器中对其进行测试并将结果添加为该问题的答案?将其置于运行 100 次的循环中,并使用Troubleshooting SPSite/SPWeb leaks in WSS v3 and MOSS 2007 中描述的 SPRequestStackTrace 注册表项来检查您的测试代码是否是问题的根源。

【讨论】:

  • 好建议,我会试一试并发布结果。
  • 引用的 MSDN 线程使用从辅助方法返回 SPWeb 引用的反模式,在此过程中犯下了新的 SPSite 的额外(和更有害的)罪。相反,应该将该代码重构为使用 SPSite/SPWeb 参数的方法:solutionizing.net/2009/01/06/elegant-spsite-elevation
【解决方案4】:

Reflector 告诉我们,当您调用 Site 属性时,这是在 SPWeb 对象内运行的代码:

public SPSite Site
{
    get
    {
        return this.m_Site;
    }
}

它不是创建一个新的 SPSite 对象,只是返回它已经拥有的对象,如果需要,这将由 SPWeb.Dispose() 来处理。因此,您可以安全地使用它,并且我会避免将其丢弃,以免 SPWeb 依赖项对您不利。

【讨论】:

  • 我想这真的取决于 m_Site 的分配方式。使用反射器分析在 m_Site 上显示“分配者”表明它是在私有成员 SPWebConstructor() 中分配的,该成员通过内部构造函数的 SPWebs 列表在站点中传递......所以最终 SPWeb 永远不会更新 SPSite。
【解决方案5】:

您是否尝试使用 SPDisposeCheck 检查您的程序集?也许它会给你一个提示如何处理你的问题。

【讨论】:

  • SPDisposeCheck 仅在我相信的“最佳实践”页面上验证这些错误。
【解决方案6】:

如果调用 depose 不会使我调用它的任何东西崩溃,我经常在我的 SharePoint 代码中使用此模式作为经验法则。我遵循的另一条规则是我尽量不要创建不会导致SPRequest 对象净收益的扩展(SPRequest 对象是与所有重量级 com 对象对话的 .net 对象)

现在分解你的例子

 for (int i = 0; i < 100; i++)
 {
     using (SPWeb web = SPContext.Current.Site.OpenWeb(""))
     {
            SPSite site = web.Site;
            Debug.WriteLine(site.Url);
     }
 }

这里的关键是SPContext.Current.Site SPContext 将在它自身正确后进行清理(少数几个这样做的对象之一),因为我们知道该站点已正确清理我希望网络已清理到,但是这远不是您问题的正确答案。 (请注意,您应该泄露通过 OpenWeb 获得的网络)

public static void GetWebPartFromCatalog(this SPWeb web, string webPartName)

您需要获取 SPSite 才能使用 Web 部件。

  1. web.Site 属性实际上会在其自身被释放后进行清理。
  2. 为什么不直接传入 SPSite 对象,让函数的用户操心呢? 在大多数情况下,我认为他们无论如何都会使用 SPContext.Current.Site 进行调用。
  3. 大多数时候我不得不担心权限问题,并不是我们的每个 dll 都在 GAC 中,所以当我编写一个新的扩展方法时,我最终不得不将它包装成 SPSecurity.CodeToRunElevated

考虑到这些,我最终会将其写为 . . .现在 Dispose 检查将在这种情况下进行,因为 SPSite 作为参数传入,因为它无法追踪它在哪个范围内被释放。

public static void GetWebPartFromCatalog(this SPSite site, string webPartName) {
     SPSecurity.CodeToRunElevated( () => {
         using(SPSite suSite = new SPSite(site.Id)){
             //do what you need to do
         }
     };
}

【讨论】:

  • 我认为您的意思是 SPSecurity.RunWithElevatedPrivileges - CodeToRunElevated 是委托类型。也就是说,默认情况下在辅助方法中提升让我觉得是个坏主意。如果你确实需要提升,你最好使用模拟:solutionizing.net/2009/01/06/elegant-spsite-elevation
【解决方案7】:

Stefan Gobner 对此有一篇出色的文章: Troubleshooting SPSite/SPWeb leaks in WSS v3 and MOSS 2007

...并确保在您的代码库中使用 SPDispose check tool

【讨论】:

    【解决方案8】:

    如果SPSite/SPWeb实例是新对象则需要删除

    例如

    using(SPSite site = new SPSite("url"))
    {
        DoSomething;
    }
    

    here using 将负责删除对象 但是,如果您已从 SPContext.Current 获得对象的引用,则不应显式处置该对象,因为该引用应可从 SPContext.Current 获得。

    【讨论】:

      猜你喜欢
      • 2011-11-23
      • 1970-01-01
      • 2011-09-13
      • 2015-09-07
      • 1970-01-01
      • 1970-01-01
      • 2018-05-03
      • 1970-01-01
      相关资源
      最近更新 更多