Eliminación de objetos
Última modificación: jueves, 08 de abril de 2010
Hace referencia a: SharePoint Foundation 2010
En este artículo
Introducción al uso de objetos descartables de SharePoint
Búsqueda de objetos eliminados incorrectamente
Técnicas de codificación para garantizar la eliminación de objetos
Objetos SPSite
Objetos SPWeb
Otros objetos que requieren eliminación
Conclusión
Introducción al uso de objetos descartables de SharePoint
Los objetos de los modelos de objetos de Microsoft SharePoint Foundation 2010 y Microsoft SharePoint Server 2010 actúan como una interfaz para trabajar con datos de SharePoint Foundation. Con frecuencia, los programadores llaman al modelo de objetos para leer datos desde o escribir nuevos datos en los almacenes de datos de SharePoint Foundation 2010 y SharePoint Server 2010.
Los modelos de objetos de SharePoint Foundation 2010 y SharePoint Server 2010 contienen objetos que implementan la interfaz IDisposable. Debe tomar precauciones al usar estos objetos para evitar su retención a largo plazo en la memoria en Microsoft .NET Framework.
En concreto, debe eliminar explícitamente los objetos de SharePoint que implementan IDisposable cuando haya terminado de usarlos.
En escenarios en los que usa muchos objetos de SharePoint (por ejemplo, en sitios de SharePoint que usan elementos web personalizados), se pueden ocasionar los siguientes comportamientos inusuales si no elimina los objetos de SharePoint cuando haya terminado de usarlos.
Reciclajes frecuentes del grupo de aplicaciones de SharePoint Foundation, especialmente durante los períodos de mayor uso
Bloqueos de aplicaciones que aparecen como daños en el montón en el depurador
Uso intensivo de memoria para procesos de trabajo de Internet Information Services (IIS)
Rendimiento deficiente del sistema y aplicaciones
En este artículo se proporciona una guía sobre los procedimientos adecuados para la manipulación y eliminación de objetos de SharePoint que implementan IDispose. Los temas tratados en este artículo también los marca SharePoint Dispose Checker Tool, un programa gratuito disponible como descarga, que inspecciona los ensamblados en busca de prácticas de codificación que puedan causar pérdidas de memoria debido al tratamiento o eliminación incorrectos de objetos de SharePoint.
¿Por qué eliminar?
Muchos de los objetos de SharePoint Foundation, principalmente los objetos de las clases SPSite y SPWeb, se crean como objetos administrados. Sin embargo, estos objetos usan código y memoria no administrados para realizar la mayor parte de su trabajo. La parte administrada del objeto es mucho menor que la parte no administrada. Debido a que la parte más pequeña administrada no realiza una demanda intensiva de memoria al recolector de elementos no utilizados, éste no libera el objeto de la memoria a tiempo. El uso que hace el objeto de una gran cantidad de memoria no administrada puede provocar algunos de los comportamientos inusuales descritos anteriormente. Si llama a aplicaciones que trabajan con objetos IDisposable en SharePoint Foundation debe eliminar los objetos cuando las aplicaciones hayan terminado de usarlos. No debe confiar en el recolector de elementos no utilizados para que los libere de la memoria automáticamente.
Búsqueda de objetos eliminados incorrectamente
Para identificar la posible presencia de objetos eliminados incorrectamente realice las siguientes preguntas:
¿El grupo de aplicaciones se recicla con frecuencia, especialmente con cargas elevadas (suponiendo que el grupo de aplicaciones está configurado para reciclarse cuando se alcanza un umbral de memoria)?
El umbral de memoria debería estar entre 800 megabytes (MB) y 1,5 gigabytes (GB), suponiendo que hay al menos 2 GB de RAM. Si se configura el reciclaje del grupo de aplicaciones para que tenga lugar cuando el uso de memoria esté más cerca de 1 GB, se obtendrán mejores resultados; pero debería realizar pruebas para determinar qué configuración es mejor para su entorno. Si la configuración de reciclaje es demasiado baja, el sistema tendrá problemas de rendimiento debido a que el grupo de aplicaciones se reciclará muy frecuentemente. Si el valor es demasiado alto, el sistema experimentará problemas de rendimiento debido a intercambios de página, fragmentación de memoria y otros problemas.
¿El sistema tiene un rendimiento deficiente, especialmente con cargas elevadas?
A medida que el uso de memoria comienza a aumentar, el sistema debe compensar, por ejemplo, mediante memoria de paginación y controlando la fragmentación de memoria.
¿El sistema se bloquea o los usuarios experimentan errores inesperados tales como tiempos de espera o errores de páginas no disponibles, especialmente con cargas elevadas?
Nuevamente, cuando el uso de memoria aumenta o se fragmenta, se producen errores en algunas funciones porque no pueden asignar memoria para otras operaciones. En muchos casos, el código no controla adecuadamente la excepción "memoria insuficiente", lo que provoca errores falsos o engañosos.
¿Su sistema usa elementos web personalizados o de terceros, o aplicaciones personalizadas?
Es posible que no sepa que estos elementos web deben eliminar objetos de SharePoint ni por qué deben hacerlo, y que suponga que la recolección de elementos no utilizados realiza esta función automáticamente. Sin embargo, esto no sucede así en todos los casos.
Si la respuesta a la pregunta número 4 y a una o más de las otras preguntas es "sí", es muy probable que el código personalizado no esté eliminando los elementos correctamente.
Si los sitios muestran alguno de los comportamientos inusuales descritos anteriormente, puede determinar si la causa es una pérdida de memoria debida a los objetos eliminados incorrectamente al comprobar si los registros ULS (disponibles en C:\Program Files\Common Files\microsoft shared\Web Server Extensions\14\LOGS) tienen entradas relacionadas con el objeto SPRequest. Cada instancia de SPSite y SPWeb contiene una referencia a un objeto SPRequest que, a su vez, contiene una referencia a un objeto COM no administrado que controla la comunicación con el servidor de bases de datos. SharePoint Foundation supervisa el número de objetos SPRequest que existen en cada subproceso específico y en subprocesos paralelos, y agrega entradas útiles a los registros en los siguientes tres escenarios:
El número total de objetos SPRequest supera un umbral configurable.
Un objeto SPRequest continúa existiendo al final de un subproceso.
La recolección de objetos no utilizados quitó un objeto SPRequest del montón.
El primer escenario se produce con más frecuencia, especialmente si el sitio usa el valor de umbral predeterminado de ocho objetos SPRequest. Cuando el número de objetos SPRequest supera este umbral, la siguiente entrada aparece en los registros ULS:
"Número posiblemente excesivo de objetos SPRequest (número de objetos) actualmente sin liberar en el subproceso número de subproceso. Asegúrese de que este objeto o su elemento primario (como un objeto SPWeb o SPSite) se elimine apropiadamente. Identificador de asignación para este objeto: {GUID}"
El mejor umbral varía según la naturaleza de su sitio y las aplicaciones que se ejecutan en él. Cuando los sitios tienen problemas de rendimiento, debe supervisar los registros ULS de la instalación para saber cuántos objetos SPRequest están creando las aplicaciones del sitio. Esto ayuda a determinar si los diseños de los sitios y aplicaciones están creando demasiados objetos SPRequest. Incluso si la eliminación incorrecta de objetos no es la causa del problema de rendimiento, es posible que deba rediseñar los sitios o aplicaciones de sitio personalizadas para reducir el consumo de memoria global causado por la proliferación excesiva de objetos SPRequest.
Debido a que el umbral predeterminado muy bajo no se aplica a muchos sitios, puede cambiar este umbral mediante la edición de la siguiente subclave del Registro:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\HeapSettings
LocalSPRequestWarnCount = valor de umbral deseado
Después de determinar que la eliminación incorrecta de objetos podría estar provocando la proliferación de objetos SPRequest y aumentando innecesariamente el consumo de memoria de los sitios, puede encontrar instancias específicas de eliminación incorrecta mediante la búsqueda de las siguientes dos entradas. Ambos mensajes indican casos en los que se desperdicia memoria debido a la eliminación incorrecta de objetos de SharePoint y ambos se relacionan con el número y el estado de objetos SPRequest en un único subproceso:
"Un objeto SPRequest no se eliminó antes del final de este subproceso. Para no desperdiciar recursos del sistema, elimine este objeto o su elemento primario (como SPSite o SPWeb) ni bien haya terminado de usarlo. Este objeto se eliminará ahora. Identificador de asignación: {GUID}. Para determinar dónde se ha asignado este objeto, cree una subclave del Registro en HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\HeapSettings. A continuación, cree un nuevo valor DWORD denominado SPRequestStackTrace con el valor 1 bajo esta clave".
Este mensaje indica que se ha eliminado un objeto SPRequest porque aún existía al final de un subproceso.
"El recolector de elementos no utilizados recuperó un objeto SPRequest en lugar de liberarlo explícitamente. Para no desperdiciar recursos del sistema, elimine este objeto o su elemento primario (como SPSite o SPWeb) ni bien haya terminado de usarlo. Identificador de asignación: {GUID}. Para determinar dónde se ha asignado este objeto, cree una clave del Registro en HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\HeapSettings. A continuación, cree un nuevo valor DWORD denominado SPRequestStackTrace con el valor 1 bajo esta clave".
Este mensaje indica que el recolector de elementos no utilizados eliminó un objeto SPRequest.
Para identificar el código que causa el problema, puede buscar en los registros entradas que contengan los identificadores de asignación, o seguir las instrucciones que aparecen en las advertencias y agregar el siguiente valor de configuración de subclave al Registro:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\HeapSettings SPRequestStackTrace = 1
Este valor de configuración de subclave garantiza que el seguimiento de la pila de la asignación original de SPRequest (que se produce siempre que se crea un objeto SPSite o SPWeb) se agregue a los registros cuando se produzcan estas advertencias.
En las secciones siguientes se describen varias técnicas de codificación que puede usar para garantizar que los objetos se eliminen correctamente.
Técnicas de codificación para garantizar la eliminación de objetos
Puede emplear determinadas técnicas de codificación para garantizar la eliminación de objetos. Estas técnicas incluyen el uso de los siguientes elementos en el código:
El método Dispose
La instrucción using
Los bloques try, catch y finally
Uso de los métodos Dispose y Close
Los métodos Dispose y Close para los objetos SPWeb y SPSite funcionan de la misma manera. El método Dispose llama al método Close del objeto. Se recomienda llamar al método Dispose, en lugar de Close, porque los objetos SPWeb y SPSite implementan la interfaz IDisposable y la recolección de elementos no utilizados estándar de .NET Framework llama al método Dispose para liberar de la memoria los recursos asociados con el objeto.
La instrucción using
Puede eliminar automáticamente objetos de SharePoint que implementan la interfaz IDisposable mediante la instrucción using de Microsoft Visual C# y Visual Basic.
El código siguiente proporciona un ejemplo.
String str;
using(SPSite oSPsite = new SPSite("https://server"))
{
using(SPWeb oSPWeb = oSPSite.OpenWeb())
{
str = oSPWeb.Title;
str = oSPWeb.Url;
}
}
Dim str As String
Using oSPsite As New SPSite("https://server")
Using oSPWeb As SPWeb = oSPSite.OpenWeb()
str = oSPWeb.Title
str = oSPWeb.Url
End Using
End Using
Puede simplificar en gran medida el código si aprovecha las instrucciones using. Como se indicó en la Referencia de C# (en el tema sobre la instrucción using), Common Language Runtime convierte las instrucciones using en bloques try and finally y se eliminan los objetos que implementan la interfaz IDisposable. Sin embargo, en muchos casos no se recomienda el uso de instrucciones using o se deben usar con precaución y comprendiendo lo que hace el tiempo de ejecución. En el siguiente ejemplo de código, se muestra un caso en el que no es deseable que el tiempo de ejecución construya un bloque finally y elimine objetos. En este caso, SPContext devuelve un objeto SPWeb.
// Do not do this. Dispose() is automatically called on SPWeb.
using( SPWeb web = SPControl.GetContextWeb(HttpContext.Current)) { ... }
' Do not do this. Dispose() is automatically called on SPWeb.
Using web As SPWeb = SPControl.GetContextWeb(HttpContext.Current)
'.......
End Using
SharePoint Framework administra los objetos SPContext y no se deben eliminar explícitamente en el código. Esto ocurre también con los objetos SPSite y SPWeb que devuelven SPContext.Site, SPContext.Current.Site, SPContext.Web y SPContext.Current.Web.
Nota
Este procedimiento recomendado resuelve el problema que la herramienta SharePoint Dispose Checker Tool identifica como SPDisposeCheckID_220.
Debe tener precaución y ser consciente de lo que hace el tiempo de ejecución cada vez que se combinan llamadas al modelo de objetos de SharePoint en la misma línea. Las pérdidas que surgen en este escenario se encuentran entre las más difíciles de encontrar.
En el ejemplo de código siguiente, se crea una instancia de un objeto SPSite pero no se elimina, porque el tiempo de ejecución solamente garantiza la eliminación del objeto SPWeb que devuelve OpenWeb.
void CombiningCallsLeak()
{
using (SPWeb web = new SPSite(SPContext.Current.Web.Url).OpenWeb())
{
// ... New SPSite will be leaked.
} // SPWeb object web.Dispose() automatically called.
}
Private Sub CombiningCallsLeak()
Using web As SPWeb = New SPSite(SPContext.Current.Web.Url).OpenWeb()
' ... New SPSite will be leaked.
End Using ' SPWeb object web.Dispose() automatically called.
End Sub
Para corregir este problema, anide una instrucción using dentro de otra.
void CombiningCallsBestPractice()
{
using (SPSite siteCollection = new SPSite(SPContext.Current.Web.Url))
{
using (SPWeb web = siteCollection.OpenWeb())
{
// Perform operations on site.
} // SPWeb object web.Dispose() automatically called.
} // SPSite object siteCollection.Dispose() automatically called.
}
Private Sub CombiningCallsBestPractice()
Using siteCollection As New SPSite(SPContext.Current.Web.Url)
Using web As SPWeb = siteCollection.OpenWeb()
' Perform operations on site.
End Using ' SPWeb object web.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
Si no realiza ninguna operación en el objeto SPSite, podría escribir esto más brevemente, como en el siguiente ejemplo de código.
void CombiningCallsBestPractice()
{
using (SPSite siteCollection = new SPSite(SPContext.Current.Web.Url))
using (SPWeb web = siteCollection.OpenWeb())
{
// Perform operations on site.
} // SPWeb object web.Dispose() automatically called; SPSite object
// siteCollection.Dispose() automatically called.
}
Private Sub CombiningCallsBestPractice()
Using siteCollection As New SPSite(SPContext.Current.Web.Url)
Using web As SPWeb = siteCollection.OpenWeb()
' Perform operations on site.
End Using ' SPWeb object web.Dispose() automatically called; SPSite object
End Using
' siteCollection.Dispose() automatically called.
End Sub
En otros casos, debe construir sus propios bloques try, catch y finally. Los ejemplos más obvios son escenarios en los que debe controlar excepciones y, por lo tanto, debe incluir un bloque catch. En la siguiente sección se proporcionan instrucciones acerca de cuándo y cómo usar los bloques try, catch y finally.
Los bloques try, catch y finally
El uso de los bloques try, catch y finally tiene sentido cuando debe controlar excepciones. Cualquier código dentro de un bloque try/catch debe tener una cláusula finally que rija, que garantice que se eliminen los objetos que implementan IDisposable. Observe que en el siguiente ejemplo de código debe rellenar el bloque catch con código que controla la excepción. Nunca deje vacío un bloque catch. Tenga en cuenta también el procedimiento recomendado para comprobación de null antes de la eliminación.
String str;
SPSite oSPSite = null;
SPWeb oSPWeb = null;
try
{
oSPSite = new SPSite("https://server");
oSPWeb = oSPSite.OpenWeb(..);
str = oSPWeb.Title;
}
catch(Exception e)
{
// Handle exception, log exception, etc.
}
finally
{
if (oSPWeb != null)
oSPWeb.Dispose();
if (oSPSite != null)
oSPSite.Dispose();
}
Dim str As String
Dim oSPSite As SPSite = Nothing
Dim oSPWeb As SPWeb = Nothing
Try
oSPSite = New SPSite("https://server")
oSPWeb = oSPSite.OpenWeb(..)
str = oSPWeb.Title
Catch e As Exception
' Handle exception, log exception, etc.
Finally
If oSPWeb IsNot Nothing Then
oSPWeb.Dispose()
End If
If oSPSite IsNot Nothing Then
oSPSite.Dispose()
End If
End Try
Se necesitarían los bloques Try y finally o una instrucción using para evitar posibles pérdidas cuando crea un objeto descartable dentro de un bloque foreach, como se muestra en el siguiente ejemplo de código.
public static void SPSiteCollectionForEachBestPractice()
{
string sUrl = "http://spvm";
using (SPSite siteCollectionOuter = new SPSite(sUrl))
{
SPWebApplication webApp = siteCollectionOuter.WebApplication;
SPSiteCollection siteCollections = webApp.Sites;
SPSite siteCollectionInner = null;
foreach (siteCollectionInner in siteCollections)
{
try // Should be first statement after foreach.
{
Console.WriteLine(siteCollectionInner.Url);
// Exception occurs here.
}
finally
{
if(siteCollectionInner != null)
siteCollectionInner.Dispose();
}
}
} // SPSite object siteCollectionOuter.Dispose() automatically called.
}
Public Shared Sub SPSiteCollectionForEachBestPractice()
Dim sUrl As String = "http://spvm"
Using siteCollectionOuter As New SPSite(sUrl)
Dim webApp As SPWebApplication = siteCollectionOuter.WebApplication
Dim siteCollections As SPSiteCollection = webApp.Sites
Dim siteCollectionInner As SPSite = Nothing
For Each siteCollectionInner In siteCollections
Try ' Should be first statement after foreach.
Console.WriteLine(siteCollectionInner.Url)
' Exception occurs here.
Finally
If siteCollectionInner IsNot Nothing Then
siteCollectionInner.Dispose()
End If
End Try
Next
End Using
End Sub ' SPSite object siteCollectionOuter.Dispose() automatically called.
Response.Redirect con los bloques try, catch y finally, e instrucciones using
El bloque finally se ejecuta después de llamar a Response.Redirect en el bloque try. Response.Redirect, en última instancia, genera una excepción ThreadAbortException. Cuando se genera esta excepción, el tiempo de ejecución ejecuta todos los bloques finally antes de terminar el subproceso. Sin embargo, debido a que el bloque finally puede realizar una cantidad ilimitada de operaciones o cancelar ThreadAbortException, el subproceso no necesariamente finalizará. Por lo tanto, para que pueda tener lugar cualquier redirección o la transferencia de procesamiento, debe eliminar los objetos. Si el código debe realizar una redirección, impleméntelo de forma similar al siguiente ejemplo de código.
String str;
SPSite oSPSite = null;
SPWeb oSPWeb = null;
try
{
oSPSite = new SPSite("https://server");
oSPWeb = oSPSite.OpenWeb(..);
str = oSPWeb.Title;
if(bDoRedirection)
{
if (oSPWeb != null)
oSPWeb.Dispose();
if (oSPSite != null)
oSPSite.Dispose();
Response.Redirect("newpage.aspx");
}
}
catch(Exception e)
{
}
finally
{
if (oSPWeb != null)
oSPWeb.Dispose();
if (oSPSite != null)
oSPSite.Dispose();
}
Dim str As String
Dim oSPSite As SPSite = Nothing
Dim oSPWeb As SPWeb = Nothing
Try
oSPSite = New SPSite("https://server")
oSPWeb = oSPSite.OpenWeb(..)
str = oSPWeb.Title
If bDoRedirection Then
If oSPWeb IsNot Nothing Then
oSPWeb.Dispose()
End If
If oSPSite IsNot Nothing Then
oSPSite.Dispose()
End If
Response.Redirect("newpage.aspx")
End If
Catch e As Exception
Finally
If oSPWeb IsNot Nothing Then
oSPWeb.Dispose()
End If
If oSPSite IsNot Nothing Then
oSPSite.Dispose()
End If
End Try
Debido a que una instrucción using indica al tiempo de ejecución que cree un bloque finally, cuando usa Response.Redirect de una instrucción using, asegúrese de que los objetos se eliminan correctamente. En el ejemplo de código siguiente se muestra cómo hacerlo.
using (SPSite oSPSite = new SPSite("https://server"))
using (SPWeb oSPWeb = oSPSite.OpenWeb(..))
{
if (bDoRedirection)
Response.Redirect("newpage.aspx");
}
Using oSPSite As New SPSite("https://server")
Using oSPWeb As SPWeb = oSPSite.OpenWeb(..)
If bDoRedirection Then
Response.Redirect("newpage.aspx")
End If
End Using
End Using
Recomendaciones para reducir la retención a largo plazo de objetos
Para reducir la retención a largo plazo de objetos de SharePoint, siga estas recomendaciones generales.
Si crea el objeto con un operador new, asegúrese de que la aplicación de creación lo elimine.
Nota
Este procedimiento recomendado resuelve el problema que la herramienta SharePoint Dispose Checker Tool identifica como SPDisposeCheckID_110.
Procedimiento recomendado de codificación nº 1
Eliminación explícita
void CreatingSPSiteExplicitDisposeNoLeak() { SPSite siteCollection = null; try { siteCollection = new SPSite("http://moss"); } finally { if (siteCollection != null) siteCollection.Dispose(); } }
Private Sub CreatingSPSiteExplicitDisposeNoLeak() Dim siteCollection As SPSite = Nothing Try siteCollection = New SPSite("http://moss") Finally If siteCollection IsNot Nothing Then siteCollection.Dispose() End If End Try End Sub
Procedimiento recomendado de codificación nº 2
Eliminación automática
CreatingSPSiteWithAutomaticDisposeNoLeak() { using (SPSite siteCollection = new SPSite("http://moss")) { } // SPSite object siteCollection.Dispose() is called automatically. }
CreatingSPSiteWithAutomaticDisposeNoLeak() { using (SPSite siteCollection = new SPSite("http://moss")) { } // SPSite object siteCollection.Dispose() is called automatically. }
Eliminación de elementos creados por los métodos de SharePoint que devuelven otros objetos SPWeb (como OpenWeb()).
Nota
Este procedimiento recomendado resuelve el problema que la herramienta SharePoint Dispose Checker Tool identifica como SPDisposeCheckID_120.
Procedimiento recomendado de codificación
void OpenWebNoLeak() { using (SPSite siteCollection = new SPSite("http://moss")) { using (SPWeb web = siteCollection.OpenWeb()) { } // SPWeb object web.Dispose() automatically called. } // SPSite object siteCollection.Dispose() automatically called. }
Private Sub OpenWebNoLeak() Using siteCollection As New SPSite("http://moss") Using web As SPWeb = siteCollection.OpenWeb() End Using ' SPWeb object web.Dispose() automatically called. End Using ' SPSite object siteCollection.Dispose() automatically called. End Sub
No comparta ningún objeto SPRequest (y por consiguiente, ningún objeto que contenga una referencia a un objeto SPRequest) entre subprocesos. Las técnicas de codificación que comparten un objeto SPRequest entre dos o más subprocesos o crean un objeto SPRequest en un subproceso y lo eliminan en otro, no son compatibles. Esto significa que no se pueden almacenar objetos que contengan una referencia a un objeto SPRequest en una variable estática. Por lo tanto, no se deben almacenar objetos de SharePoint que implementan IDisposable (por ejemplo, SPWeb o SPSite) en variables estáticas.
Objetos SPSite
En esta sección se describen situaciones en las que se devuelven nuevos objetos SPSite y deben eliminarse.
En general, cada vez que una aplicación de llamada usa los nuevos constructores SPSite (cualquier firma), debería llamar al método Dispose() cuando haya terminado de usar el objeto. Si el objeto SPSite se obtiene de GetContextSite(), la aplicación de llamada no debe eliminar el objeto. Debido a que los objetos SPWeb y SPSite mantienen una lista interna que se deriva de este modo, la eliminación del objeto puede hacer que el modelo de objetos de SharePoint se comporte de manera inesperada. Internamente, SharePoint Foundation enumera esta lista una vez que se haya completado la página para eliminar los objetos correctamente.
Clase SPSiteCollection
En esta sección se describen los métodos, propiedades u operadores del objeto SPSiteCollection que requieren que se cierre el objeto devuelto SPSite después de obtener acceso.
Método SPSiteCollection.Add
El método SPSiteCollection.Add crea y devuelve un nuevo objeto SPSite. Debería eliminar los objetos SPSite devueltos desde el método SPSiteCollection.Add.
Nota
Este procedimiento recomendado resuelve el problema que la herramienta SharePoint Dispose Checker Tool identifica como SPDisposeCheckID_240.
Procedimiento no recomendado de codificación
void SPSiteCollectionAddLeak()
{
SPWebApplication webApp = new SPSite("http://moss").WebApplication;
SPSiteCollection siteCollections = webApp.Sites;
SPSite siteCollection = siteCollections.Add("sites/myNewSiteCollection", "DOMAIN\\User",
"roger.lamb@litwareinc.com");
// SPSite siteCollection leak.
}
Private Sub SPSiteCollectionAddLeak()
Dim webApp As SPWebApplication = New SPSite("http://moss").WebApplication
Dim siteCollections As SPSiteCollection = webApp.Sites
Dim siteCollection As SPSite = siteCollections.Add("sites/myNewSiteCollection", "DOMAIN\User", "roger.lamb@litwareinc.com")
' SPSite siteCollection leak.
End Sub
Procedimiento recomendado de codificación
void SPSiteCollectionAddNoLeak()
{
SPWebApplication webApp = new SPSite("http://moss").WebApplication;
SPSiteCollection siteCollections = webApp.Sites;
using (SPSite siteCollection = siteCollections.Add("sites/myNewSiteCollection", "DOMAIN\\User",
"roger.lamb@litwareinc.com"))
{
} // SPSite object siteCollection.Dispose() automatically called.
}
Private Sub SPSiteCollectionAddNoLeak()
Dim webApp As SPWebApplication = New SPSite("http://moss").WebApplication
Dim siteCollections As SPSiteCollection = webApp.Sites
Using siteCollection As SPSite = siteCollections.Add("sites/myNewSiteCollection", "DOMAIN\User", "roger.lamb@litwareinc.com")
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
Operador de índice SPSiteCollection [ ]
El operador de índice SPSiteCollection [] devuelve un nuevo objeto SPSite para cada acceso. Incluso si ya se ha obtenido acceso a ese objeto, se crea una instancia de SPSite. En los ejemplos de código siguiente se muestra la eliminación incorrecta del objeto SPSite.
Nota
Este procedimiento recomendado resuelve el problema que la herramienta SharePoint Dispose Checker Tool identifica como SPDisposeCheckID_230.
Procedimiento no recomendado de codificación nº 1
Uso del operador de índice
void SPSiteCollectionIndexerLeak()
{
using (SPSite siteCollectionOuter = new SPSite("http://moss"))
{
SPWebApplication webApp = siteCollectionOuter.WebApplication;
SPSiteCollection siteCollections = webApp.Sites;
SPSite siteCollectionInner = siteCollections[0];
// SPSite siteCollectionInner leak.
} // SPSite object siteCollectionOuter.Dispose() automatically called.
}
Private Sub SPSiteCollectionIndexerLeak()
Using siteCollectionOuter As New SPSite("http://moss")
Dim webApp As SPWebApplication = siteCollectionOuter.WebApplication
Dim siteCollections As SPSiteCollection = webApp.Sites
Dim siteCollectionInner As SPSite = siteCollections(0)
' SPSite siteCollectionInner leak.
End Using ' SPSite object siteCollectionOuter.Dispose() automatically called.
End Sub
Procedimiento no recomendado de codificación nº 2
Uso del bucle foreach
void SPSiteCollectionForEachLeak()
{
using (SPSite siteCollectionOuter = new SPSite("http://moss"))
{
SPWebApplication webApp = siteCollectionOuter.WebApplication;
SPSiteCollection siteCollections = webApp.Sites;
foreach (SPSite siteCollectionInner in siteCollections)
{
// SPSite siteCollectionInner leak.
}
} // SPSite object siteCollectionOuter.Dispose() automatically called.
}
Private Sub SPSiteCollectionForEachLeak()
Using siteCollectionOuter As New SPSite("http://moss")
Dim webApp As SPWebApplication = siteCollectionOuter.WebApplication
Dim siteCollections As SPSiteCollection = webApp.Sites
For Each siteCollectionInner As SPSite In siteCollections
' SPSite siteCollectionInner leak.
Next siteCollectionInner
End Using ' SPSite object siteCollectionOuter.Dispose() automatically called.
End Sub
Procedimiento recomendado de codificación nº 1
Uso del operador de índice
void SPSiteCollectionIndexerNoLeak()
{
using (SPSite siteCollectionOuter = new SPSite("http://moss"))
{
SPSite siteCollectionInner = null;
try
{
SPWebApplication webApp = siteCollectionOuter.WebApplication;
SPSiteCollection siteCollections = webApp.Sites;
siteCollectionInner = siteCollections[0];
}
finally
{
if (siteCollectionInner != null)
siteCollectionInner.Dispose();
}
} // SPSite object siteCollectionOuter.Dispose() automatically called.
}
Private Sub SPSiteCollectionIndexerNoLeak()
Using siteCollectionOuter As New SPSite("http://moss")
Dim siteCollectionInner As SPSite = Nothing
Try
Dim webApp As SPWebApplication = siteCollectionOuter.WebApplication
Dim siteCollections As SPSiteCollection = webApp.Sites
siteCollectionInner = siteCollections(0)
Finally
If siteCollectionInner IsNot Nothing Then
siteCollectionInner.Dispose()
End If
End Try
End Using ' SPSite object siteCollectionOuter.Dispose() automatically called.
End Sub
Procedimiento recomendado de codificación nº 2
Uso del bucle foreach
void SPSiteCollectionForEachNoLeak()
{
using (SPSite siteCollectionOuter = new SPSite("http://yoursite”))
{
SPWebApplication webApp = siteCollectionOuter.WebApplication;
SPSiteCollection siteCollections = webApp.Sites;
foreach (SPSite siteCollectionInner in siteCollections)
{
try
{
// ...
}
finally
{
if(siteCollectionInner != null)
siteCollectionInner.Dispose();
}
}
} // SPSite object siteCollectionOuter.Dispose() automatically called.
}
Private Sub SPSiteCollectionForEachNoLeak()
Using siteCollectionOuter As SPSite = New SPSite("http://yoursite")
Dim webApp As SPWebApplication = siteCollectionOuter.WebApplication
Dim siteCollections As SPSiteCollection = webApp.Sites
For Each siteCollectionInner As SPSite In siteCollections
Try
' ...
Finally
If siteCollectionInner IsNot Nothing Then
siteCollectionInner.Dispose()
End If
End Try
Next
End Using
End Sub
Propiedad SPSite.AllWebs (SPWebCollection)
En esta sección se describen los métodos, propiedades u operadores de la colección de propiedades AllWebs que requieren que se cierre el objeto SPWeb después de obtener acceso.
Método SPSite.AllWebs.Add
El método SPSite.AllWebs.Add crea y devuelve un objeto SPWeb. Debería eliminar los objetos SPWeb devueltos desde SPSite.AllWebs.Add.
Nota
Este procedimiento recomendado resuelve el problema que la herramienta SharePoint Dispose Checker Tool identifica como SPDisposeCheckID_150.
Procedimiento no recomendado de codificación
void AllWebsAddLeak()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
SPWeb web = siteCollection.AllWebs.Add("site-relative URL");
// SPWeb object leaked.
} // SPSite object siteCollection.Dispose() automatically called.
}
Private Sub AllWebsAddLeak()
Using siteCollection As New SPSite("http://moss")
Dim web As SPWeb = siteCollection.AllWebs.Add("site-relative URL")
' SPWeb object leaked.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
Procedimiento recomendado de codificación
void AllWebsAddNoLeak()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb web = siteCollection.AllWebs.Add("site-relative URL"))
{
} // SPWeb object web.Dispose() automatically called.
} // SPSite object siteCollection.Dispose() automatically called.
}
Private Sub AllWebsAddNoLeak()
Using siteCollection As New SPSite("http://moss")
Using web As SPWeb = siteCollection.AllWebs.Add("site-relative URL")
End Using ' SPWeb object web.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
Método SPWebCollection.Add
El método SPWebCollection.Add crea y devuelve un objeto SPWeb que se debe eliminar.
Nota
Este procedimiento recomendado resuelve el problema que la herramienta SharePoint Dispose Checker Tool identifica como SPDisposeCheckID_200.
Procedimiento no recomendado de codificación
void SPWebCollectionAddLeak(string strWebUrl)
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb outerWeb = siteCollection.OpenWeb())
{
SPWebCollection webCollection = siteCollection.AllWebs; // No AllWebs leak just getting reference.
SPWeb innerWeb = webCollection.Add(strWebUrl); // Must dispose innerWeb.
// innerWeb leak.
} // SPWeb object outerWeb.Dispose() automatically called.
} // SPSite object siteCollection.Dispose() automatically called.
}
Private Sub SPWebCollectionAddLeak(ByVal strWebUrl As String)
Using siteCollection As New SPSite("http://moss")
Using outerWeb As SPWeb = siteCollection.OpenWeb()
Dim webCollection As SPWebCollection = siteCollection.AllWebs ' No AllWebs leak just getting reference.
Dim innerWeb As SPWeb = webCollection.Add(strWebUrl) ' Must dispose innerWeb.
' innerWeb leak.
End Using ' SPWeb object outerWeb.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
Procedimiento recomendado de codificación
void SPWebCollectionAddNoLeak(string strWebUrl)
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb outerWeb = siteCollection.OpenWeb())
{
SPWebCollection webCollection = siteCollection.AllWebs; // No AllWebs leak just getting reference.
using (SPWeb innerWeb = webCollection.Add(strWebUrl))
{
//...
}
} // SPWeb object outerWeb.Dispose() automatically called.
} // SPSite object siteCollection.Dispose() automatically called.
}
Private Sub SPWebCollectionAddNoLeak(ByVal strWebUrl As String)
Using siteCollection As New SPSite("http://moss")
Using outerWeb As SPWeb = siteCollection.OpenWeb()
Dim webCollection As SPWebCollection = siteCollection.AllWebs ' No AllWebs leak just getting reference.
Using innerWeb As SPWeb = webCollection.Add(strWebUrl)
'...
End Using
End Using ' SPWeb object outerWeb.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
Operador de índice SPSite.AllWebs [ ]
El operador de índice SPSite.AllWebs [] devuelve una nueva instancia de SPWeb cada vez que se obtiene acceso a él. Incluso si ya se ha obtenido acceso a ese objeto, se crea un objeto durante la operación de indización. Si no se cerró correctamente, los siguientes ejemplos de código dejan un objeto SPWeb en el recolector de elementos no utilizados de .NET Framework.
Nota
Este procedimiento recomendado resuelve el problema que la herramienta SharePoint Dispose Checker Tool identifica como SPDisposeCheckID_130.
Procedimiento no recomendado de codificación
void AllWebsForEachLeak()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb outerWeb = siteCollection.OpenWeb())
{
foreach (SPWeb innerWeb in siteCollection.AllWebs)
{
// Explicitly dispose here to avoid out of memory leaks with large number of SPWeb objects.
}
} // SPWeb object outerWeb.Dispose() automatically called.
} // SPSite object siteCollection.Dispose() automatically called.
}
Private Sub AllWebsForEachLeak()
Using siteCollection As New SPSite("http://moss")
Using outerWeb As SPWeb = siteCollection.OpenWeb()
For Each innerWeb As SPWeb In siteCollection.AllWebs
' Explicitly dispose here to avoid out of memory leaks with large number of SPWeb objects.
Next
End Using ' SPWeb object outerWeb.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
Procedimiento recomendado de codificación nº 1
Uso del bucle foreach
void AllWebsForEachNoLeakOrMemoryOOM()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb outerWeb = siteCollection.OpenWeb())
{
foreach (SPWeb innerWeb in siteCollection.AllWebs)
{
try
{
// ...
}
finally
{
if(innerWeb != null)
innerWeb.Dispose();
}
}
} // SPWeb object outerWeb.Dispose() automatically called.
} // SPSite object siteCollection.Dispose() automatically called.
}
Private Sub AllWebsForEachNoLeakOrMemoryOOM()
Using siteCollection As New SPSite("http://moss")
Using outerWeb As SPWeb = siteCollection.OpenWeb()
For Each innerWeb As SPWeb In siteCollection.AllWebs
Try
' ...
Finally
If innerWeb IsNot Nothing Then
innerWeb.Dispose()
End If
End Try
Next
End Using ' SPWeb object outerWeb.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
Procedimiento recomendado de codificación nº 2
Uso del operador de índice
void AllWebsIndexerNoLeak()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb web = siteCollection.AllWebs[0])
{
} // SPWeb object web.Dispose() automatically called.
} // SPSite object siteCollection.Dispose() automatically called.
}
Private Sub AllWebsIndexerNoLeak()
Using siteCollection As New SPSite("http://moss")
Using web As SPWeb = siteCollection.AllWebs(0)
End Using ' SPWeb object web.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
Métodos SPSite.OpenWeb y SPSite. SelfServiceCreateSite
Los métodos OpenWeb() y SelfServiceCreateSite (todas las firmas) del objeto SPSite crean un objeto SPWeb y lo devuelven al llamador. Este nuevo objeto no se almacena en el objeto SPSite y no se elimina en ningún lugar de la clase SPSite. Por este motivo, debe eliminar los objetos creados a través de estos métodos.
Procedimiento no recomendado de codificación
void OpenWebLeak()
{
using (SPWeb web = new SPSite(SPContext.Current.Web.Url).OpenWeb())
{
// SPSite leaked !
} // SPWeb object web.Dispose() automatically called.
}
Private Sub OpenWebLeak()
Using web As SPWeb = New SPSite(SPContext.Current.Web.Url).OpenWeb()
' SPSite leaked !
End Using ' SPWeb object web.Dispose() automatically called.
End Sub
Procedimiento recomendado de codificación
void OpenWebNoLeak()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb web = siteCollection.OpenWeb())
{
} // SPWeb object web.Dispose() automatically called.
} // SPSite object siteCollection.Dispose() automatically called.
}
Private Sub OpenWebNoLeak()
Using siteCollection As New SPSite("http://moss")
Using web As SPWeb = siteCollection.OpenWeb()
End Using ' SPWeb object web.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
Propiedad SPSite.RootWeb
En instrucciones anteriores se indicaba que la aplicación de llamada debía eliminar la propiedad SPSite.RootWeb justo antes de eliminar el objeto SPSite que la estaba usando. Éstas ya no son las instrucciones oficiales. La limpieza de eliminación se controla automáticamente mediante SharePoint Foundation y SharePoint Server. Además, las propiedades LockIssue, Owner y SecondaryContact de SPSite usaban la propiedad RootWeb internamente. Debido a las instrucciones actualizadas para RootWeb, ya no se recomienda llamar al método Dispose en la propiedad SPSite.RootWeb cuando se usa una de estas propiedades.
Nota
Este procedimiento recomendado resuelve el problema que la herramienta SharePoint Dispose Checker Tool identifica como SPDisposeCheckID_140.
Procedimiento recomendado de codificación
public void RootWebBestPractice()
{
// New SPSite.
using (SPSite siteCollection = new SPSite("http://moss"))
{
SPWeb rootWeb1 = siteCollection.RootWeb;
// No explicit rootWeb1 dispose required.
} // siteCollection automatically disposed by implementing using().
// rootWeb1 will be Disposed by SPSite.
// SPContext and SPControl
SPWeb rootWeb2 = SPContext.Current.Site.RootWeb;
// Also would apply to SPControl.GetContextSite(Context);
// No explicit rootWeb2 dispose required because it is obtained from SPContext.Current.Site.
}
Public Sub RootWebBestPractice()
' New SPSite.
Using siteCollection As New SPSite("http://moss")
Dim rootWeb1 As SPWeb = siteCollection.RootWeb
' No explicit rootWeb1 dispose required.
End Using ' siteCollection automatically disposed by implementing using().
' rootWeb1 will be Disposed by SPSite.
' SPContext and SPControl
Dim rootWeb2 As SPWeb = SPContext.Current.Site.RootWeb
' Also would apply to SPControl.GetContextSite(Context);
' No explicit rootWeb2 dispose required because it is obtained from SPContext.Current.Site.
End Sub
Microsoft.Office.Server.UserProfiles.PersonalSite (solo Office SharePoint Server 2007)
Microsoft.Office.Server.UserProfiles.PersonalSite devuelve un objeto SPSite que se debe eliminar.
Nota
Este procedimiento recomendado resuelve el problema que la herramienta SharePoint Dispose Checker Tool identifica como SPDisposeCheckID_400.
Procedimiento no recomendado de codificación
void PersonalSiteLeak()
{
// Open a site collection.
using (SPSite siteCollection = new SPSite("http://moss"))
{
UserProfileManager profileManager = new UserProfileManager(ServerContext.GetContext(siteCollection));
UserProfile profile = profileManager.GetUserProfile("domain\\username");
SPSite personalSite = profile.PersonalSite; // Will leak.
}
}
Private Sub PersonalSiteLeak()
' Open a site collection.
Using siteCollection As New SPSite("http://moss")
Dim profileManager As New UserProfileManager(ServerContext.GetContext(siteCollection))
Dim profile As UserProfile = profileManager.GetUserProfile("domain\username")
Dim personalSite As SPSite = profile.PersonalSite ' Will leak.
End Using
End Sub
Procedimiento recomendado de codificación
void PersonalSiteNoLeak()
{
// Open a site collection.
using (SPSite siteCollection = new SPSite("http://moss"))
{
UserProfileManager profileManager = new UserProfileManager(ServerContext.GetContext(siteCollection));
UserProfile profile = profileManager.GetUserProfile("domain\\username");
using (SPSite personalSite = profile.PersonalSite)
{
// ...
}
}
}
Private Sub PersonalSiteNoLeak()
' Open a site collection.
Using siteCollection As New SPSite("http://moss")
Dim profileManager As New UserProfileManager(ServerContext.GetContext(siteCollection))
Dim profile As UserProfile = profileManager.GetUserProfile("domain\username")
Using personalSite As SPSite = profile.PersonalSite
' ...
End Using
End Using
End Sub
En otro caso límite, UserProfiles.PersonalSite tiene pérdidas, como se muestra en el ejemplo de código siguiente.
void PersonalSiteLeak()
{
// Open a site collection.
using (SPSite siteCollection = new SPSite("http://moss"))
{
UserProfileManager profileManager = new UserProfileManager(ServerContext.GetContext(siteCollection));
UserProfile profile = profileManager.GetUserProfile("domain\\username");
SPSite personalSite = profile.PersonalSite; // Will leak.
}
}
Private Sub PersonalSiteLeak()
' Open a site collection.
Using siteCollection As New SPSite("http://moss")
Dim profileManager As New UserProfileManager(ServerContext.GetContext(siteCollection))
Dim profile As UserProfile = profileManager.GetUserProfile("domain\username")
Dim personalSite As SPSite = profile.PersonalSite ' Will leak.
End Using
End Sub
Para resolver este tipo de pérdidas, siga el modelo que se muestra en el siguiente ejemplo de código.
void PersonalSiteNoLeak()
{
// Open a site collection
using (SPSite siteCollection = new SPSite("http://moss"))
{
UserProfileManager profileManager = new UserProfileManager(ServerContext.GetContext(siteCollection));
UserProfile profile = profileManager.GetUserProfile("domain\\username");
using (SPSite personalSite = profile.PersonalSite)
{
// ...
}
}
}
Private Sub PersonalSiteNoLeak()
' Open a site collection
Using siteCollection As New SPSite("http://moss")
Dim profileManager As New UserProfileManager(ServerContext.GetContext(siteCollection))
Dim profile As UserProfile = profileManager.GetUserProfile("domain\username")
Using personalSite As SPSite = profile.PersonalSite
' ...
End Using
End Using
End Sub
Observe también que puede mejorar el rendimiento (y evitar la creación de un objeto SPSite) si recupera un objeto PersonalSite de ProfileLoader, como se muestra en el ejemplo de código siguiente.
UserProfile myProfile = ProfileLoader.GetProfileLoader().GetUserProfile();
using (SPSite personalSite = myProfile.PersonalSite)
{
// ...
}
Dim myProfile As UserProfile = ProfileLoader.GetProfileLoader().GetUserProfile()
Using personalSite As SPSite = myProfile.PersonalSite
' ...
End Using
Además, si va a crear un elemento web para un Mi sitio, puede usar una instancia de PersonalSite que no necesita ser eliminada.
IPersonalPage currentMySitePage = this.Page as IPersonalPage;
if (currentMySitePage != null && !currentMySitePage.IsProfileError)
{
SPSite personalSite = currentMySitePage.PersonalSite; // Will not leak.
// ...
}
Dim currentMySitePage As IPersonalPage = TryCast(Me.Page, IPersonalPage)
If currentMySitePage IsNot Nothing AndAlso (Not currentMySitePage.IsProfileError) Then
Dim personalSite As SPSite = currentMySitePage.PersonalSite ' Will not leak.
' ...
End If
Objetos SPWeb
En esta sección se describen las situaciones en las que se devuelven objetos SPWeb y es posible que se deban eliminar.
Propiedad SPWeb.ParentWeb
Instrucciones actualizadas
En instrucciones anteriores se recomendaba que la aplicación de llamada eliminara SPWeb.ParentWeb. Éstas ya no son las instrucciones oficiales. La limpieza de eliminación se controla automáticamente mediante SharePoint Foundation y SharePoint Server.
Nota
Este procedimiento recomendado resuelve el problema que la herramienta SharePoint Dispose Checker Tool identifica como SPDisposeCheckID_170.
Procedimiento recomendado de codificación
using (SPSite site = new SPSite("https://localhost"))
{
using (SPWeb web = site.OpenWeb())
{
SPList list = web.Lists["Announcements"];
SPWeb parentWeb = list.ParentWeb; //No explicit dispose required.
}
}
Using site As New SPSite("https://localhost")
Using web As SPWeb = site.OpenWeb()
Dim list As SPList = web.Lists("Announcements")
Dim parentWeb As SPWeb = list.ParentWeb 'No explicit dispose required.
End Using
End Using
Propiedad SPWeb.Webs
En esta sección se describen los métodos, propiedades u operadores de la colección de propiedades Webs que requieren que se elimine el objeto SPWeb después de obtener acceso.
SPWeb.Webs
La propiedad SPWeb.Webs devuelve un objeto SPWebCollection. Los objetos SPWeb de esta colección deben eliminarse.
Nota
Este procedimiento recomendado resuelve el problema que la herramienta SharePoint Dispose Checker Tool identifica como SPDisposeCheckID_180.
Procedimiento no recomendado de codificación
void WebsLeak()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb outerWeb = siteCollection.OpenWeb())
{
foreach (SPWeb innerWeb in outerWeb.Webs)
{
// SPWeb innerWeb leak.
}
} // SPWeb object outerWeb.Dispose() automatically called.
} // SPSite object siteCollection.Dispose() automatically called.
}
Private Sub WebsLeak()
Using siteCollection As New SPSite("http://moss")
Using outerWeb As SPWeb = siteCollection.OpenWeb()
For Each innerWeb As SPWeb In outerWeb.Webs
' SPWeb innerWeb leak.
Next
End Using ' SPWeb object outerWeb.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
Procedimiento recomendado de codificación
void WebsNoLeak()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb outerWeb = siteCollection.OpenWeb())
{
foreach (SPWeb innerWeb in outerWeb.Webs)
{
try // Should be first statement after foreach.
{
// ...
}
finally
{
if(innerWeb != null)
innerWeb.Dispose();
}
}
} // SPWeb object outerWeb.Dispose() automatically called.
} // SPSite object siteCollection.Dispose() automatically called.
}
Private Sub WebsNoLeak()
Using siteCollection As New SPSite("http://moss")
Using outerWeb As SPWeb = siteCollection.OpenWeb()
For Each innerWeb As SPWeb In outerWeb.Webs
Try ' Should be first statement after foreach.
' ...
Finally
If innerWeb IsNot Nothing Then
innerWeb.Dispose()
End If
End Try
Next
End Using ' SPWeb object outerWeb.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
SPWeb.Webs.Add
El método SPWeb.Webs.Add (o SPWebCollection.Add) crea y devuelve un nuevo objeto SPWeb. Debería eliminar los objetos SPWeb que devuelve esta llamada al método.
Nota
Este procedimiento recomendado resuelve el problema que la herramienta SharePoint Dispose Checker Tool identifica como SPDisposeCheckID_190.
Procedimiento no recomendado de codificación
void WebsAddLeak(string strWebUrl)
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb web = siteCollection.OpenWeb())
{
SPWeb addedWeb = web.Webs.Add(strWebUrl); // Will leak.
} // SPWeb object web.Dispose() automatically called.
} // SPSite object siteCollection.Dispose() automatically called.
}
Private Sub WebsAddLeak(ByVal strWebUrl As String)
Using siteCollection As New SPSite("http://moss")
Using web As SPWeb = siteCollection.OpenWeb()
Dim addedWeb As SPWeb = web.Webs.Add(strWebUrl) ' Will leak.
End Using ' SPWeb object web.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
Procedimiento recomendado de codificación
void WebsAddNoLeak(string strWebUrl)
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb web = siteCollection.OpenWeb())
{
using (SPWeb addedWeb = web.Webs.Add(strWebUrl))
{
//..
}
} // SPWeb object web.Dispose() automatically called.
} // SPSite object siteCollection.Dispose() automatically called.
}
Private Sub WebsAddNoLeak(ByVal strWebUrl As String)
Using siteCollection As New SPSite("http://moss")
Using web As SPWeb = siteCollection.OpenWeb()
Using addedWeb As SPWeb = web.Webs.Add(strWebUrl)
'..
End Using
End Using ' SPWeb object web.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
Operador de índice SPWeb.Webs[]
El operador de índice SPWeb.Webs[] devuelve un nuevo objeto SPWeb para cada acceso. Incluso si ya se ha obtenido acceso a ese objeto, se crea un SPWeb mediante la llamada al método OpenWeb. Los siguientes ejemplos de código ocasionan la retención a largo plazo de estos objetos en memoria que usa .NET Framework.
Procedimiento no recomendado de codificación nº 1
Uso del bucle For
int i;
SPWeb oSPWeb, oSPWeb2;
SPSite oSPSite = SPControl.GetContextSite(Context);
oSPWeb = oSPSite.OpenWeb();
for(i = 0;i < oSPWeb.Webs.Count;i++)
{
oSPWeb2 = oSPWeb.Webs[i];
BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title);
}
Dim i As Integer
Dim oSPWeb, oSPWeb2 As SPWeb
Dim oSPSite As SPSite = SPControl.GetContextSite(Context)
oSPWeb = oSPSite.OpenWeb()
For i = 0 To oSPWeb.Webs.Count - 1
oSPWeb2 = oSPWeb.Webs(i)
BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title)
Next i
Procedimiento no recomendado de codificación nº 2
Uso del bucle foreach
SPWeb oSPWeb, oSPWeb2;
SPSite oSPSite = SPControl.GetContextSite(Context);
oSPWeb = oSPSite.OpenWeb();
foreach(SPWeb oSPWeb2 in oSPWebe.Webs)
{
BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title);
}
Dim oSPWeb, oSPWeb2 As SPWeb
Dim oSPSite As SPSite = SPControl.GetContextSite(Context)
oSPWeb = oSPSite.OpenWeb()
For Each oSPWeb2 As SPWeb In oSPWebe.Webs
BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title)
Next
La corrección recomendada consiste en eliminar al final de cada bucle.
Procedimiento recomendado de codificación nº 1
Uso del bucle For
int i;
SPWeb oSPWeb, oSPWeb2;
SPSite oSPSite = SPControl.GetContextSite(Context);
oSPWeb = oSPSite.OpenWeb();
for(i = 0;i < oSPWeb.Webs.Count;i++)
{
oSPWeb2 = oSPWeb.Webs[i];
BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title);
oSPWeb2.Dispose();
}
oSPWeb.Dispose();
Dim i As Integer
Dim oSPWeb, oSPWeb2 As SPWeb
Dim oSPSite As SPSite = SPControl.GetContextSite(Context)
oSPWeb = oSPSite.OpenWeb()
For i = 0 To oSPWeb.Webs.Count - 1
oSPWeb2 = oSPWeb.Webs(i)
BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title)
oSPWeb2.Dispose()
Next i
oSPWeb.Dispose()
Procedimiento recomendado de codificación nº 2
Uso del bucle foreach
SPWeb oSPWeb, oSPWeb2;
SPSite oSPSite = SPControl.GetContextSite(Context);
oSPWeb = oSPSite.OpenWeb();
foreach(SPWeb oSPWeb2 in oSPWeb.Webs)
{
BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title);
oSPWeb2.Dispose();
}
oSPWeb.Dispose();
Dim oSPWeb, oSPWeb2 As SPWeb
Dim oSPSite As SPSite = SPControl.GetContextSite(Context)
oSPWeb = oSPSite.OpenWeb()
For Each oSPWeb2 As SPWeb In oSPWeb.Webs
BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title)
oSPWeb2.Dispose()
Next
oSPWeb.Dispose()
Procedimiento recomendado de codificación nº 3
Uso del bucle for con eliminación automática
int i;
SPWeb oSPWeb, oSPWeb2;
SPSite oSPSite = SPControl.GetContextSite(Context);
using(oSPWeb = oSPSite.OpenWeb())
{
for(i = 0;i < oSPWeb.Webs.Count;i++)
{
Using(oSPWeb2 = oSPWeb.Webs[i])
{
BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title);
}
}
}
Dim i As Integer
Dim oSPSite As SPSite = SPControl.GetContextSite(Context)
Using oSPWeb As SPWeb = oSPSite.OpenWeb()
For i = 0 To oSPWeb.Webs.Count - 1
Using oSPWeb2 As SPWeb = oSPWeb.Webs(i)
BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title)
End Using
Next
End Using
Otros objetos que requieren eliminación
En esta sección se indica cuándo se debe llamar al método Dispose en otros objetos de SharePoint.
Propiedad Microsoft.SharePoint.Portal.SiteData.Area.Web
La propiedad Web de la clase SharePoint.Portal.SiteData.Area devuelve un nuevo objeto SPWeb cada vez que se tiene acceso a ella. Cada uso de la propiedad Area.Web debería tener una correspondiente llamada al método Dispose. Aunque las clases Area y AreaManager ahora son obsoletas, esto sigue siendo una preocupación al migrar código heredado.
Nota
Este procedimiento recomendado resuelve el problema que la herramienta SharePoint Dispose Checker Tool identifica como SPDisposeCheckID_500.
Procedimiento no recomendado de codificación
void AreaWebLeak()
{
// AreaManager and Area are obsolete in SharePoint Server, but this
// should still be noted.
Area area = AreaManager.GetArea(PortalContext.Current, new Guid("{GUID}"));
string str = area.Web.Title;
// SPWeb area.Web leak.
}
Private Sub AreaWebLeak()
' AreaManager and Area are obsolete in SharePoint Server, but this
' should still be noted.
Dim area As Area = AreaManager.GetArea(PortalContext.Current, New Guid("{GUID}"))
Dim str As String = area.Web.Title
' SPWeb area.Web leak.
End Sub
Procedimiento recomendado de codificación
public void AreaWebNoLeak()
{
// AreaManager and Area are obsolete but this should still be noted.
Area area = AreaManager.GetArea(PortalContext.Current, new Guid("{GUID}"));
using (SPWeb areaWeb = area.Web)
{
string str = areaWeb.Title;
}
}
Public Sub AreaWebNoLeak()
' AreaManager and Area are obsolete but this should still be noted.
Dim area As Area = AreaManager.GetArea(PortalContext.Current, New Guid("{GUID}"))
Using areaWeb As SPWeb = area.Web
Dim str As String = areaWeb.Title
End Using
End Sub
Métodos SPControl.GetContextSite y SPControl.GetContextWeb
Si el objeto se obtiene de los objetos de contexto de SharePoint (los métodos GetContextSite y GetContextWeb de la clase SPControl), la aplicación de llamada no debe llamar al método Dispose en el objeto. Si lo hace, puede ocasionar que el modelo de objetos de SharePoint se comporte de forma inesperada o se produzca un error. Esto se debe a una lista interna que se mantiene en los objetos SPSite y SPWeb derivados de este modo. Internamente, el modelo de objetos enumera esta lista una vez que se haya completado la página para eliminar los objetos correctamente.
Aún debería eliminar un objeto que se crea a partir de estos objetos, por ejemplo, si se abre un sitio web desde un objeto SPSite que obtuvo mediante el método GetContextSite.
Nota
Este procedimiento recomendado resuelve el problema que la herramienta SharePoint Dispose Checker Tool identifica como SPDisposeCheckID_210.
Procedimiento no recomendado de codificación
void SPControlBADPractice()
{
SPSite siteCollection = SPControl.GetContextSite(Context);
siteCollection.Dispose(); // DO NOT DO THIS.
SPWeb web = SPControl.GetContextWeb(Context);
web.Dispose(); // DO NOT DO THIS.
}
Private Sub SPControlBADPractice()
Dim siteCollection As SPSite = SPControl.GetContextSite(Context)
siteCollection.Dispose() ' DO NOT DO THIS.
Dim web As SPWeb = SPControl.GetContextWeb(Context)
web.Dispose() ' DO NOT DO THIS.
End Sub
Procedimiento recomendado de codificación
void SPControlBestPractice()
{
SPSite siteCollection = SPControl.GetContextSite(Context);
SPWeb web = SPControl.GetContextWeb(Context);
// Do NOT call Dispose().
}
Private Sub SPControlBestPractice()
Dim siteCollection As SPSite = SPControl.GetContextSite(Context)
Dim web As SPWeb = SPControl.GetContextWeb(Context)
' Do NOT call Dispose().
End Sub
Microsoft.SharePoint.WebPartPages.SPLimitedWebPartManager
La clase SPLimitedWebPartManager contiene una referencia a un objeto interno SPWeb que debe eliminarse.
Nota
Este procedimiento recomendado resuelve el problema que la herramienta SharePoint Dispose Checker Tool identifica como SPDisposeCheckID_160.
Procedimiento no recomendado de codificación
void SPLimitedWebPartManagerLeak()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb web = siteCollection.OpenWeb())
{
SPFile page = web.GetFile("Source_Folder_Name/Source_Page");
SPLimitedWebPartManager webPartManager =
page.GetLimitedWebPartManager(PersonalizationScope.Shared);
// SPWeb object webPartManager.Web leaked.
} // SPWeb object web.Dispose() automatically called.
} // SPSite object siteCollection.Dispose() automatically called.
}
Private Sub SPLimitedWebPartManagerLeak()
Using siteCollection As New SPSite("http://moss")
Using web As SPWeb = siteCollection.OpenWeb()
Dim page As SPFile = web.GetFile("Source_Folder_Name/Source_Page")
Dim webPartManager As SPLimitedWebPartManager = page.GetLimitedWebPartManager(PersonalizationScope.Shared)
' SPWeb object webPartManager.Web leaked.
End Using ' SPWeb object web.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
Procedimiento recomendado de codificación
void SPLimitedWebPartManagerLeak()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb web = siteCollection.OpenWeb())
{
SPFile page = web.GetFile("Source_Folder_Name/Source_Page");
SPLimitedWebPartManager webPartManager =
page.GetLimitedWebPartManager(PersonalizationScope.Shared);
webPartManaber.Web.Dispose();
} // SPWeb object web.Dispose() automatically called.
} // SPSite object siteCollection.Dispose() automatically called.
}
Private Sub SPLimitedWebPartManagerLeak()
Using siteCollection As New SPSite("http://moss")
Using web As SPWeb = siteCollection.OpenWeb()
Dim page As SPFile = web.GetFile("Source_Folder_Name/Source_Page")
Dim webPartManager As SPLimitedWebPartManager = page.GetLimitedWebPartManager(PersonalizationScope.Shared)
webPartManaber.Web.Dispose()
End Using ' SPWeb object web.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
Microsoft.SharePoint.Publishing.PublishingWeb
Nota
El espacio de nombres Microsoft.SharePoint.Publishing pertenece a SharePoint Server 2010. Esta sección se aplica a SharePoint Server 2010 y no a SharePoint Foundation 2010.
El método GetPublishingWebs de la clase PublishingWeb devuelve un objeto PublishingWebCollection. Debe llamar al método Close en cada objeto enumerado innerPubWeb. Cuando llama solo al método GetPublishingWeb, no es necesario que llame a Close.
Nota
Este procedimiento recomendado resuelve el problema que la herramienta SharePoint Dispose Checker Tool identifica como SPDisposeCheckID_300.
Procedimiento no recomendado de codificación
void PublishingWebCollectionLeak()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb web = siteCollection.OpenWeb())
{
// Passing in SPWeb object that you own, no dispose needed on
// outerPubWeb.
PublishingWeb outerPubWeb = PublishingWeb.GetPublishingWeb(web);
PublishingWebCollection pubWebCollection = outerPubWeb.GetPublishingWebs();
foreach (PublishingWeb innerPubWeb in pubWebCollection)
{
// innerPubWeb leak.
}
// PublishingWeb will leak for each innerPubWeb referenced
} // SPWeb object web.Dispose() automatically called.
} // SPSite object siteCollection.Dispose() automatically called.
}
Private Sub PublishingWebCollectionLeak()
Using siteCollection As New SPSite("http://moss")
Using web As SPWeb = siteCollection.OpenWeb()
' Passing in SPWeb object that you own, no dispose needed on
' outerPubWeb.
Dim outerPubWeb As PublishingWeb = PublishingWeb.GetPublishingWeb(web)
Dim pubWebCollection As PublishingWebCollection = outerPubWeb.GetPublishingWebs()
For Each innerPubWeb As PublishingWeb In pubWebCollection
' innerPubWeb leak.
Next
' PublishingWeb will leak for each innerPubWeb referenced
End Using ' SPWeb object web.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
Procedimiento recomendado de codificación
void PublishingWebCollectionNoLeak()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb web = siteCollection.OpenWeb())
{
// Passing in SPWeb object that you own, no dispose needed on
// outerPubWeb.
PublishingWeb outerPubWeb = PublishingWeb.GetPublishingWeb(web);
PublishingWebCollection pubWebCollection = outerPubWeb.GetPublishingWebs();
foreach (PublishingWeb innerPubWeb in pubWebCollection)
{
try
{
// ...
}
finally
{
if(innerPubWeb != null)
innerPubWeb.Close();
}
}
} // SPWeb object web.Dispose() automatically called.
} // SPSite object siteCollection.Dispose() automatically called.
}
Private Sub PublishingWebCollectionNoLeak()
Using siteCollection As New SPSite("http://moss")
Using web As SPWeb = siteCollection.OpenWeb()
' Passing in SPWeb object that you own, no dispose needed on
' outerPubWeb.
Dim outerPubWeb As PublishingWeb = PublishingWeb.GetPublishingWeb(web)
Dim pubWebCollection As PublishingWebCollection = outerPubWeb.GetPublishingWebs()
For Each innerPubWeb As PublishingWeb In pubWebCollection
Try
' ...
Finally
If innerPubWeb IsNot Nothing Then
innerPubWeb.Close()
End If
End Try
Next
End Using ' SPWeb object web.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
Nota
Existe un requisito similar para llamar a Close en cada objeto PublishingWeb creado mediante la llamada al método Add en el PublishingWebCollection devuelto mediante Microsoft.SharePoint.Publishing.PublishingWeb.GetPublishingWebs. Para obtener un ejemplo de código, vea el método GetPublishingWebs(). Este procedimiento recomendado resuelve el problema que la herramienta SharePoint Dispose Checker Tool identifica como SPDisposeCheckID_310.
El método Microsoft.SharePoint.Publishing.PublishingWeb.GetVariation devuelve un objeto PublishingWeb que se debe eliminar.
Nota
Este procedimiento recomendado resuelve el problema que la herramienta SharePoint Dispose Checker Tool identifica como SPDisposeCheckID_320.
Procedimiento no recomendado de codificación
void GetVariationLeak()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb web = siteCollection.OpenWeb())
{
PublishingWeb publishingWeb = PublishingWeb.GetPublishingWeb(web); // Passing in SPWeb object, so no Close() needed
VariationLabel variationLabel = Variations.Current.UserAccessibleLabels[0];
PublishingWeb variationPublishingWeb = publishingWeb.GetVariation(variationLabel); // Must be Closed().
// ...
} // SPWeb object outerWeb.Dispose() automatically called.
} // SPSite object siteCollection.Dispose() automatically called.
}
Private Sub GetVariationLeak()
Using siteCollection As New SPSite("http://moss")
Using web As SPWeb = siteCollection.OpenWeb()
Dim publishingWeb As PublishingWeb = PublishingWeb.GetPublishingWeb(web) ' Passing in SPWeb object, so no Close() needed
Dim variationLabel As VariationLabel = Variations.Current.UserAccessibleLabels(0)
Dim variationPublishingWeb As PublishingWeb = publishingWeb.GetVariation(variationLabel) ' Must be Closed().
' ...
End Using ' SPWeb object outerWeb.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
Procedimiento recomendado de codificación
void GetVariationNoLeak()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb web = siteCollection.OpenWeb())
{
PublishingWeb variationPublishingWeb = null;
try
{
PublishingWeb publishingWeb = PublishingWeb.GetPublishingWeb(web); // Passing in SPWeb object, so no Close() needed.
VariationLabel variationLabel = Variations.Current.UserAccessibleLabels[0];
variationPublishingWeb = publishingWeb.GetVariation(variationLabel); // Must be Closed().
// ...
}
finally
{
if(variationPublishingWeb != null)
variationPublishingWeb.Close();
}
} // SPWeb object web.Dispose() automatically called.
} // SPSite object siteCollection.Dispose() automatically called.
}
Private Sub GetVariationNoLeak()
Using siteCollection As New SPSite("http://moss")
Using web As SPWeb = siteCollection.OpenWeb()
Dim variationPublishingWeb As PublishingWeb = Nothing
Try
Dim publishingWeb As PublishingWeb = PublishingWeb.GetPublishingWeb(web) ' Passing in SPWeb object, so no Close() needed.
Dim variationLabel As VariationLabel = Variations.Current.UserAccessibleLabels(0)
variationPublishingWeb = publishingWeb.GetVariation(variationLabel) ' Must be Closed().
' ...
Finally
If variationPublishingWeb IsNot Nothing Then
variationPublishingWeb.Close()
End If
End Try
End Using ' SPWeb object web.Dispose() automatically called.
End Using ' SPSite object siteCollection.Dispose() automatically called.
End Sub
Modelos de eliminación entre métodos
En el ejemplo siguiente se muestra la práctica común de retención de los objetos SPSite y SPWeb entre métodos de una clase. A veces este modelo de diseño es necesario, pero asegúrese de no olvidar el momento adecuado para llamar a Dispose cuando termine de realizar las llamadas entre métodos. En el ejemplo de código siguiente se muestra un modelo en el que SPSite y SPWeb tienen pérdidas cuando la clase se sale del ámbito.
public class CrossMethodLeak
{
private SPSite _siteCollection = null;
private SPWeb _web = null;
public void MethodA()
{
_siteCollection = new SPSite("http://moss");
_web = _siteCollection.OpenWeb();
}
public void MethodB()
{
if (_web != null)
{
string title = _web.Title;
}
}
public void MethodC()
{
if (_web != null)
{
string name = _web.Name;
}
}
}
Public Class CrossMethodLeak
Private _siteCollection As SPSite = Nothing
Private _web As SPWeb = Nothing
Public Sub MethodA()
_siteCollection = New SPSite("http://moss")
_web = _siteCollection.OpenWeb()
End Sub
Public Sub MethodB()
If _web IsNot Nothing Then
Dim title As String = _web.Title
End If
End Sub
Public Sub MethodC()
If _web IsNot Nothing Then
Dim name As String = _web.Name
End If
End Sub
End Class
Conclusión
Debido a que muchos objetos de SharePoint implementan la interfaz IDisposable, debe tener cuidado al usar estos objetos para evitar su retención en la memoria. Si sigue las instrucciones de eliminación de objetos de SharePoint, como se describe en este artículo, puede ayudar a garantizar la confiabilidad del código personalizado.
Agradecimientos
Nos gustaría agradecer a las siguientes personas por sus aportes y orientación en la creación de este artículo:
Steve Sheppard, Microsoft Corporation
Chris Gideon, Microsoft Corporation
Rashid Aga, Microsoft Corporation
Vea también
Otros recursos
SharePoint Dispose Checker Tool
Guía de SharePoint de Patterns & Practices
Centro para desarrolladores de Windows SharePoint Services
Centro para desarrolladores de SharePoint Server 2007
Prácticas recomendadas de personalización de Productos y Tecnologías de SharePoint
Centro de recursos de procedimientos recomendados para SharePoint Server 2007