Evaluar y enviar comentarios
MSDN
MSDN Library
Prácticas recomendadas: Problemas de codificación habituales al usar el modelo de objetos de SharePoint

Resumen: información adicional acerca de los problemas habituales detectados por los desarrolladores que escriben código personalizado mediante el modelo de objetos de SharePoint. (7 páginas impresas)

Scott Harris, Microsoft Corporation

Mike Ammerlaan, Microsoft Corporation

Septiembre de 2007

Este artículo se aplica a: Windows SharePoint Services 3.0, Windows SharePoint Services 2.0, Microsoft Office SharePoint Server 2007

Contenido

Descripción general de los problemas de código habituales con el modelo de objetos de SharePoint

A medida que más desarrolladores escriben código personalizado mediante el modelo de objetos de SharePoint, aparecen problemas habituales que pueden afectar al rendimiento de la aplicación. En este artículo se intentan solucionar algunos de esos problemas y se recomiendan formas de identificarlos y corregirlos.

Las áreas siguientes reflejan los problemas principales que los desarrolladores han detectado al crear código personalizado mediante el modelo de objetos de SharePoint:

  • Eliminación de objetos de SharePoint

  • Almacenamiento de datos y objetos en la memoria caché

  • Creación de código escalable

Eliminación de objetos de SharePoint

Uno de los mayores problemas que un desarrollador puede encontrar al crear código personalizado mediante el modelo de objetos de SharePoint es no eliminar correctamente dichos objetos. Los detalles del problema de la completa eliminación de objetos de SharePoint están fuera del ámbito de este artículo; sin embargo, podemos ofrecer una manera de identificar el problema y proporcionar información general acerca de cómo corregirlo. Para obtener más información acerca de prácticas para la eliminación de objetos de SharePoint, consulte Recomendaciones: uso de objetos eliminables de Windows SharePoint Services

En el modelo de objetos de SharePoint, los objetos Microsoft.SharePoint.SPSite y Microsoft.SharePoint.SPWeb se crean en código administrado como un contenedor pequeño (aproximadamente 2 KB de tamaño). A continuación, este contenedor crea objetos no administrados, con un tamaño medio de aproximadamente 1-2 MB. Si su código es similar al siguiente y supone que la recopilación SPWeb.Webs tiene 10 subsitios, se crea un total de 10 elementos, cada uno con una media de 2 MB de memoria (hasta un total de 20 MB).

C#
public void GetNavigationInfo()
{
   SPWeb oSPWeb = SPContext.Web;

    // .. Get information oSPWeb for navigation .. 

   foreach(SPWeb oSubWeb in oSPWeb.GetSubWebsForCurrentUser())
   {
      // .. Add subweb information for navigation .. 
    }
}

En cargas de usuario más ligeras, el escenario anterior puede que no represente un problema; no obstante, a medida que aumenta la carga de usuarios, comienza a producirse un rendimiento deficiente, errores inesperados y, en algunos casos, un error de la aplicación SharePoint o del grupo de aplicaciones. Con el ejemplo anterior de 10 objetos creados cada vez que un usuario visita la página, se puede ver que el uso de la memoria aumenta rápidamente.

Por ejemplo, en la tabla siguiente se muestra cuánta memoria se asigna a medida que los usuarios visitan el sistema en un período de tiempo relativamente corto.

Tabla 1. Uso de memoria en el mejor y peor caso a medida que el número de usuarios aumenta

Usuarios

Mejor caso

Peor caso

10

100 MB

200 MB

50

500 MB

1000 MB

100

1000 MB

2000 MB

250

2500 MB

5000 MB

A medida que el número de los usuarios que visita el sistema aumenta, esta situación empeora. Cuando el uso de memoria aumenta, el sistema comienza a comportarse de forma extraña, incluso con un rendimiento deficiente y posiblemente con errores hasta que se recicle el grupo de aplicaciones o se emita el comando iisreset.

Cómo identificar el problema

Puede identificar fácilmente este problema si responde a las preguntas siguientes:

  1. ¿Se recicla su grupo de aplicaciones con frecuencia, especialmente con cargas pesadas?

    Se supone que el grupo de aplicaciones está establecido para reciclarse cuando se alcanza un umbral de memoria. El umbral de memoria debe estar entre 800 MB y 1,5 GB (suponiendo que dispone de 2 GB o más de RAM). La configuración del reciclaje del grupo de aplicaciones para que se efectúe más cerca de 1 GB ofrece los mejores resultados, pero debe experimentar hasta encontrar la mejor configuración para su entorno. Si la configuración de reciclaje es demasiado baja, se producirán problemas de rendimiento por los reciclajes frecuentes del grupo de aplicaciones. Si la configuración es demasiado alta, el sistema comenzará a tener problemas de rendimiento debido al intercambio de páginas, la fragmentación de memoria y otros problemas.

  2. ¿Tiene el sistema un rendimiento deficiente, especialmente con cargas pesadas?

    A medida que el uso de memoria comienza a aumentar, el sistema se debe compensar, por ejemplo, mediante la paginación de memoria y el tratamiento de la fragmentación de memoria.

  3. ¿Se bloquea el sistema o tienen los usuarios errores inesperados tales como tiempos de espera que se agotan o errores de página no disponible, especialmente con cargas pesadas?

    De nuevo, cuando el uso de la memoria es alto o fragmentado, se pueden producir errores en algunas funciones porque no se puede asignar memoria a otras operaciones. En muchos casos, el código no trata de forma adecuada la excepción de "memoria insuficiente", que provoca errores falsos o confusos.

  4. ¿Usa su sistema elementos web personalizados o elementos web de terceros?

    La mayoría de los desarrolladores de elementos web no sabe que debe descartar los objetos de SharePoint y por qué motivo. Suponen que la recopilación de elementos no usados se ocupa de esta función automáticamente, pero no es así en todos los casos.

Si contesta de manera afirmativa a la pregunta número 4 y a una o varias del resto de preguntas, hay un 90 por ciento de probabilidades de que su código personalizado no elimine los elementos correctamente. Como puede ver en la tabla 1, sólo necesita una página de uso intensivo que no elimine los elementos de forma adecuada para que se produzcan problemas. A continuación, se muestra un ejemplo de cómo corregir la función GetNavigationInfo anterior.

C#
public void GetNavigationInfo()
{
   SPWeb oSPWeb = SPContext.Web;

   foreach(SPWeb oSubWeb in oSPWeb.GetSubWebsForCurrentUser()))
   {
      // .. Add subweb information for navigation ..
      oSubWeb.Dispose();
   }
}

En el bucle foreach, los nuevos objetos SPWeb se crean cada vez que se recuperan de la recopilación. La mayoría de los desarrolladores supone que los objetos se eliminan cuando están fuera de ámbito, pero no es así si se usa el modelo de objetos de SharePoint.

También debe conocer otras cuestiones que pueden provocar problemas. Por ejemplo, después de llamar a la propiedad RootWeb en un sitio, debe descartar el objeto SPWeb que crea mediante una llamada al método RootWeb.Dispose(). Para obtener más información acerca de cómo descartar correctamente elementos, consulte Prácticas recomendadas: uso de objetos eliminables de Windows SharePoint Services.

NotaNota:

No descarte ningún elemento devuelto directamente desde las propiedades Microsoft.SharePoint.SPContext.Site o Microsoft.SharePoint.SPContext.Web. De hacerlo, se puede provocar la inestabilidad del sistema SharePoint y errores en la aplicación.

Almacenamiento de datos y objetos en la memoria caché

Muchos desarrolladores están empezando a usar los objetos de la memoria caché de Microsoft .NET Framework (por ejemplo, System.Web.Caching.Cache) para mejorar el uso de la memoria y aumentar el rendimiento general del sistema. Pero, muchos objetos no son "seguros para subprocesos" y almacenar en la memoria caché esos objetos puede provocar bloqueos de aplicaciones y errores de usuario inesperados o no relacionados.

Almacenamiento en la memoria caché de objetos de SharePoint que no son seguros para subprocesos

Los desarrolladores tratan de aumentar el rendimiento y el uso de memoria mediante el almacenamiento en la memoria caché de los objetos SPListItemCollection que se devuelven de las consultas. En general, es una buena práctica pero el objeto SPListItemCollection contiene un objeto SPWeb incrustado que no es seguro para subprocesos y no se debe guardar. Por ejemplo, suponga que el objeto SPListItemCollection se almacena en la memoria caché en el subproceso A. Después, cuando otros subprocesos tratan de leerlo, se puede producir un error en la aplicación o comportarse de forma extraña porque el objeto no es seguro para subprocesos. 

Para obtener más información, consulte la clase Microsoft.SharePoint.SPWeb.

Sin uso de la sincronización de subprocesos

Algunos desarrolladores no saben que se encuentran en un entorno de multiproceso (de forma predeterminada, Servicios de Internet Information Server es multiproceso) o cómo administrar ese entorno. En el siguiente ejemplo de código se muestra cómo algunos desarrolladores almacenan objetos en la memoria caché Microsoft.SharePoint.SPListItemCollection.

C#
public void CacheData()
{
   SPListItemCollection oListItems;

   oListItems = (SPListItemCollection)Cache["ListItemCacheName"];
   if(oListItems == null)
   {
      oListItems = DoQueryToReturnItems();
      Cache.Add("ListItemCacheName", oListItems, ..);
   }
}

En el ejemplo de código anterior, el problema es que si la consulta para obtener los datos dura 10 segundos, podría tener muchos usuarios que visitan esa página al mismo tiempo, todos ejecutando la misma consulta y tratando de actualizar el mismo objeto de la memoria caché al mismo tiempo. Esto puede provocar problemas de rendimiento porque la misma consulta quizás se ejecute 10, 50 ó 100 veces y puede ser causa de bloqueos debido a que varios subprocesos tratan de actualizar el mismo objeto simultáneamente, en concreto en equipos multiproceso, con numerosos subprocesos. Para corregir esta situación, debe cambiar el código del siguiente modo.

C#
public void CacheData()
{
   SPListItemCollection oListItems;

   lock(this)   {
      oListItems = (SPListItemCollection)Cache["ListItemCacheName"];
      if(oListItems == null)
      {
         oListItems = DoQueryToReturnItems();
         Cache.Add("ListItemCacheName", oListItems, ..);
     }
   }
}
NotaNota:

Se puede aumentar el rendimiento ligeramente si se coloca el bloqueo en el bloque de código if(oListItems == null). Al hacerlo, no es necesario suspender todos los subprocesos al comprobar si los datos ya están almacenados en la memoria caché. Según lo que tarde la consulta en devolver los datos, todavía sigue existiendo la posibilidad de que varios usuarios ejecuten la consulta al mismo tiempo. Esto tiene especial importancia en equipos multiproceso. Recuerde que cuantos más procesadores se ejecuten y más tarde la consulta en ejecutarse, es más probable que al colocar el bloqueo en el bloque de código if() se produzcan problemas. Aunque el ejemplo anterior quizás podría tener un ligero impacto en el rendimiento, es la única forma de garantizar que no habrá varias consultas en ejecución al mismo tiempo.

Este código suspende todos los demás subprocesos en una sección crítica que se ejecuta en Servicios de Internet Information Server e impide que otros subprocesos obtengan acceso al objeto en la memoria caché hasta que esté completamente creado.

El ejemplo anterior soluciona el problema de sincronización de subprocesos; sin embargo, todavía no es correcto porque almacena en la memoria caché un objeto que no es seguro para subprocesos. Para solucionar la seguridad de los subprocesos, puede guardar un objeto DataTable creado a partir del objeto SPListItemCollection. Por ejemplo, el ejemplo anterior se modificaría del modo siguiente.

C#
public void CacheData()
{
   DataTable oDataTable;
   SPListItemCollection oListItems;

   lock(this)
   {
      oDataTable = (DataTable)Cache["ListItemCacheName"];
      if(oDataTable == null)
      {
         oListItems = DoQueryToReturnItems();
         oDataTable = oListItems.GetDataTable();
         Cache.Add("ListItemCacheName", oDataTable, ..);
      }
   }
}

El código entonces obtiene los datos del objeto DataTable. Para obtener más información y ejemplos del uso del objeto DataTable, así como otras buenas ideas para el desarrollo de aplicaciones de SharePoint, consulte Sugerencias y trucos para el desarrollo con Windows SharePoint Services.

Creación de código escalable

Algunos desarrolladores no saben que deben escribir código que sea escalable y poder así controlar varios usuarios al mismo tiempo. Un buen ejemplo de esto lo constituye la creación de información de navegación personalizada para todos los sitios o subsitios en cada página o como parte de una página maestra. Por ejemplo, si tiene un sitio de SharePoint en una intranet corporativa y cada departamento tiene su propio sitio con muchos subsitios, el código quizás se parezca al siguiente.

C#
public void GetNavigationInfoForAllSitesAndWebs()
{
   foreach(SPSite oSPSite in SPContext.Current.Site.WebApplication.Sites)
   {
      using(SPWeb oSPWeb  = oSPSite.RootWeb)
      {
         AddAllWebs(oSPWeb );
      }
   }
}
C#
public void AddAllWebs(SPWeb oSPWeb)
{
   foreach(SPWeb oSubWeb in oSPWeb.Webs)
   {
      //.. Code to add items ..
      AddAllWebs(oSubWeb);
      oSubWeb.Dispose();
   }
}

Aunque el código anterior elimina los objetos de forma adecuada, todavía provoca problemas porque el código pasa por las mismas listas una y otra vez. Por ejemplo, si tiene 10 recopilaciones de sitios y un promedio de 20 sitios o subsitios por recopilación de sitios, iterará por el mismo código 200 veces. Para un número reducido de usuarios, esto quizás no provoque un rendimiento incorrecto. Pero, al agregar más usuarios al sistema, el problema empeora. En la tabla 2 se muestra esta situación.

Tabla 2. Las iteraciones aumentan a medida que lo hace el número de usuarios
Usuarios Iteraciones

10

2000

50

10000

100

200000

250

500000

El código se ejecuta para cada usuario que visite el sistema, pero los datos son iguales para todos. El impacto de esto varía en función de lo que haga el código. A veces, la repetición del código una y otra vez no provoca un problema de rendimiento; sin embargo, en el ejemplo anterior el sistema tiene que crear un objeto COM (los objetos SPSite o SPWeb se crean al recuperarlos de sus recopilaciones), recuperar los datos del objeto y, a continuación, eliminarlos para todos los elementos de la recopilación. Esto supone mucha sobrecarga de rendimiento.

¿Cómo puede hacer que este código sea más escalable u optimizado para un entorno multiusuario? Es una pregunta difícil de responder y depende de para qué esté diseñada la aplicación. Hay una serie de cuestiones que se debe tener en cuenta al responder cómo crear código más escalable:

  • ¿Son los datos estáticos (apenas cambian), algo estáticos (cambian a veces) o dinámicos (cambian constantemente)?

  • ¿Son los datos iguales para todos los usuarios o cambian? Por ejemplo, ¿cambian según el usuario que ha iniciado la sesión, la parte del sitio a la que se obtiene acceso o la época del año (información estacional)?

  • ¿Son los datos fácilmente accesibles o se requiere mucho tiempo para devolverlos? Por ejemplo, ¿se devuelven de una larga consulta de SQL o de bases de datos remotas que puedan tener alguna latencia de red en las transferencias de datos?

  • ¿Son los datos públicos o requieren un nivel de seguridad superior?

  • ¿Cuál es el tamaño de los datos?

  • ¿Está el sitio de SharePoint en un solo servidor o en una granja de servidores?

Según su respuesta a las preguntas anteriores, existen distintas formas para hacer el código más escalable y controlar varios usuarios. La intención de este artículo no es ofrecer respuestas a todas las preguntas ni escenarios, sino ofrecer unas pocas ideas que pueda aplicar a sus necesidades específicas. En las siguientes secciones, se proporciona una serie de áreas que se deben tener en cuenta.

Almacenamiento de datos sin procesar en la memoria caché

Puede almacenar en la memoria caché los datos mediante el objeto System.Web.Caching.Cache. Este objeto requiere consultar los datos una vez y almacenarlos en la memoria caché para que obtengan acceso otros usuarios.

Si los datos son constantes, puede configurar la memoria caché para cargar los datos una vez y que no caduquen hasta reiniciar la aplicación o cargarlos una vez al día para garantizar que estén actualizados. Puede crear el elemento de la memoria caché al iniciarse la aplicación, cuando se inicia la primera sesión de usuario o cuando el primer usuario trata de obtener acceso a dichos datos.

Si los datos son bastante estáticos, puede configurar los elementos en la memoria caché para que caduquen en un determinado número de segundos, minutos u horas después de crearse. Esto permite actualizar los datos en un intervalo de tiempo que resulte aceptable para los usuarios. Incluso si los datos están en la memoria caché sólo 30 segundos, en cargas pesadas todavía puede ver un aumento del rendimiento porque el código sólo se ejecuta una vez cada 30 segundos en vez de varias veces por segundo por cada usuario que visita el sistema.

Asegúrese de tener en cuenta los temas descritos anteriormente en Almacenamiento de datos y objetos en la memoria caché.

Creación de los datos antes de su visualización

Piense en el modo en que se usarán los datos de la memoria caché. Si estos datos se usan para tomar decisiones en tiempo de ejecución, colocarlos en un DataSet o DataTable puede ser la mejor forma de almacenarlos. A continuación, puede consultar en estos objetos los datos para tomar decisiones en tiempo de ejecución. Si los datos se usan para mostrar una lista, tabla o una página con formato al usuario, considere la posibilidad de crear un objeto de presentación y almacenarlo en la memoria caché. En tiempo de ejecución, sólo debe recuperar el objeto de la memoria caché y llamar a su función de representación para mostrar su contenido. También puede almacenar el resultado representado, pero esto puede provocar problemas de seguridad y el elemento en la memoria caché podría ser bastante grande, lo que provocaría mucho intercambio de páginas o fragmentación de memoria.

Almacenamiento en la memoria caché para un solo servidor o una granja de servidores

En función del modo en que esté configurado el sitio de SharePoint, puede que tenga que tratar algunos problemas de la memoria caché de forma distinta. Si los datos deben ser iguales en todos los servidores al mismo tiempo, debe asegurarse de que los mismos datos se almacenan en la memoria caché en cada servidor. Una forma de garantizarlo consiste en crear los datos en la memoria caché y almacenarlos en un servidor común o en una base de datos SQL. De nuevo, debe tener en cuenta lo que se tarda en obtener acceso a los datos y los problemas de seguridad de los datos que se almacenan en un servidor común.

También debe crear objetos de nivel de negocios que almacenen en la memoria caché datos en un servidor común y, a continuación, obtener acceso a dichos datos mediante distintas comunicaciones entre procesos que estén disponibles en objetos o en API de red.

Conclusión

Para garantizar que el sistema de SharePoint tiene el mejor rendimiento, debe poder contestar las siguientes preguntas acerca del código que crea:

  • ¿Elimina correctamente el código los objetos de SharePoint?

  • ¿Almacena el código los objetos en la memoria caché correctamente?

  • ¿Almacena el código los tipos de objeto correctos en la memoria caché?

  • ¿Usa el código sincronización de subprocesos cuando es necesario?

  • ¿Funciona el código tan eficazmente para 1000 usuarios como para 10?

Si tiene en cuenta estas cuestiones al crear el código, descubrirá que el sistema SharePoint se ejecuta de un modo más eficaz y que sus usuarios tienen una experiencia mucho mejor. También puede contribuir a evitar errores inesperados en el sistema.

Agradecimientos

Queremos agradecer a las siguientes personas su valiosa ayuda en las revisiones técnicas de este artículo: James Waymire (Microsoft Corporation), Duray Akar (Microsoft Corporation), Rashid Aga (Microsoft Corporation), Roger Lamb (Microsoft Corporation) y Jeff Lin (Microsoft Corporation).

Recursos adicionales

Contenido de la comunidad   ¿Qué es Community Content?
Agregar contenido nuevo      
Processing
© 2008 Microsoft Corporation. Reservados todos los derechos. Términos de uso  |  Marcas Registradas  |  Privacidad
Page view tracker