Nuevo modelo de desarrollo de Sharepoint. Adaptando nuestras soluciones de granja

Buenas tardes a todos,

Últimamemente se viene haciendo mucho hincapié desde Microsoft en el nuevo modelo de desarrollo de SharePoint, modelo que deja atrás el Feature Framework con el que veníamos trabajando últimamente y que se basa en un uso más intensivo de las APIs de Cliente y las aplicaciones para SharePoint. Obviamente, esta nueva propuesta va en detrimento de la API de Servidor y las soluciones de granja que habían sido la base de la extensibilidad de SharePoint en las versiones anteriores. Este nuevo modelo de desarrollo no solo se propone para la nueva plataforma de SharePoint OnLine, sino que se puede emplear igualmente en entornos On-Prem.

Aunque ya se ha comentado mucho sobre esto, los motivos principales que promueven este nuevo modelo podrían ser los siguientes:

  • La aparición de la plataforma OnLine, donde es obvio que las soluciones de granja, donde predomina la API de servidor, no tienen sitio.
  • Proponer un modelo de desarrollo que facilite la migración tanto a nuevas versiones On-Prem como a la versión OnLine, evitando los problemas de Upgrade del modelo anterior de desarrollo.
  • Evitar los riesgos que provocaba el anterior modelo de desarrollo, relacionado con el rendimiento como los leaks de memoria.

Un tema importante a tener en cuenta, es el hecho de que en los desarrollos sobre plataformas On-Prem, si bien se recomienda desde Microsoft adoptar de forma progresiva el nuevo modelo de desarrollo, todavía podremos seguir extendiendo la plataforma usando soluciones de granja, que estarán totalmente soportadas (ojo!!! no así las soluciones SandBox que están deprecated), al menos de momento. Lo que si nos recomiendan desde el equipo de producto es ir adaptando esas soluciones de granja para que nos sea más fácil adoptar poco a poco el nuevo modelo de desarrollo.

En este artículo os quiero enseñar algunos pasos que podríamos seguir para, si bien, en algunas ocasiones continuar trabajando en nuestros proyectos con el modelo clásico, intentar hacerlo de manera que vayamos desarrollando, de la forma más parecida posible, con los patrones y las buenas prácticas del nuevo modelo de desarrollo que nos proponen.

Adaptando nuestras soluciones .wsp para aproximarnos al nuevo modelo de desarrollo

En el ejemplo que vamos a ver a continuación nuestro objetivo será desplegar una solución de SharePoint que va a añadir una serie de columnas de sitio y tipos de Contenido. Vamos a ver la diferencia entre el approach que usaríamos habitualmente con el Feature Framework y la propuesta que nos hacen para ir aproximándonos al nuevo modelo de desarrollo.

En el primer caso usaríamos una serie de ficheros XML que añadirían tanto los tipos de contenido como las columnas de sitio. Los pasos habituales que seguiríamos serían básicamente los siguientes:

  1. Crear una característica
  2. Crear un elemento vacío de SharePoint
  3. Añadir la columnas de sitio al elemento recién creado
  4. Crear un elemento del tipo: “Tipo de Contenido”
  5. Establecer las columnas de dicho tipo de Contenido
  6. Si Visual Studio no lo ha hecho ya automáticamente (que es lo más lógico), añadiremos los elementos a la característica que habíamos creado.

El código de los ficheros .xml que vamos a añadir es el siguiente:

Para las columnas de sitio:

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Field ID='{6A4E55B4-3AB1-448F-B51D-01224423C71F}' Group="Ejemplo" Type='DateTime' Name='Fecha' DisplayName='Fecha' SourceID='http://schemas.microsoft.com/sharepoint/v3' StaticName='Fecha' />
  <Field ID='{014AB8C6-02AA-41EC-B5A1-CD46668E6087}' Group="Ejemplo" Type='Note' Name='Comentario' DisplayName='Comentario' NumLines='30' StaticName='Comentario' SourceID='http://schemas.microsoft.com/sharepoint/v3' RichText='TRUE' RichTextMode='FullHtml' IsolateStyles='FALSE' />
  <Field ID='{18377DDE-58DA-4668-9FC5-EAB7A7ED70FC}' Group="Ejemplo" Type='User' Name='Valorador' DisplayName='Valorador' SourceID='http://schemas.microsoft.com/sharepoint/v3' StaticName='Valorador' />
  <Field ID='{9F764C8E-D966-416D-8148-3CC0E4E88BDA}' Group="Ejemplo" Type='Text' Name='Ruta' DisplayName='Ruta' SourceID='http://schemas.microsoft.com/sharepoint/v3' StaticName='Ruta' />
</Elements>

Para los tipos de contenido:

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <!-- Tipo de contenido primario: Elemento (0x01) -->
  <ContentType ID="0x01002b1edca8d3f94d9dbeb9ca8da78b2460"
               Name="TipoContenido Ejemplo"
               Group="Ejemplo"
               Description="Descripción de ejemplo de tipo de contenido"
               Inherits="TRUE"
               Version="0">
    <FieldRefs>
      <FieldRef ID='{6A4E55B4-3AB1-448F-B51D-01224423C71F}' Name='Fecha' Required='TRUE'/>
      <FieldRef ID='{014AB8C6-02AA-41EC-B5A1-CD46668E6087}' Name='Comentario' Required='TRUE' />
      <FieldRef ID='{18377DDE-58DA-4668-9FC5-EAB7A7ED70FC}' Name='Valorador' Required='TRUE' />
      <FieldRef ID='{9F764C8E-D966-416D-8148-3CC0E4E88BDA}' Name='Ruta' Required='TRUE' />
    </FieldRefs>
  </ContentType>
</Elements>

¿Qué problemas tiene esta forma de desplegar nuestros elementos en SharePoint?. Utilizando estos ficheros xml se crean una serie de dependencias del fichero con las bases de datos de contenido que pueden darnos problemas a la hora de hacer una migración de nuestro entorno.

Captura de pantalla 2015-04-19 a las 13.33.15

Para evitar estos problemas de dependencias que se crean usando el Feature Framework de esta forma, podemos plantear otro approach, que nos aproxima a la forma de trabajar en el nuevo modelo de desarrollo. La solución, en este caso, pasaría por usar el receptor de eventos de la característica y crear los elementos programáticamente en el evento de activación de la misma.

El código que añadiríamos en el receptor de eventos sería el siguiente:

public class Feature1EventReceiver : SPFeatureReceiver
{
    // Quite las marcas de comentario al método siguiente para controlar el evento generado una vez activada una característica.

    public override void FeatureActivated(SPFeatureReceiverProperties properties)
    {
        SPSite site = properties.Feature.Parent as SPSite;

        using (SPWeb web = site.OpenWeb())
        {
            Helper.CreateTextColumn(web, "Ejemplo1", 255);
            Helper.CreateNumberColumn(web, "EjemploNumber1", 0);

            Helper.CreateContentType(web, "EjemploTipoContenido");
            Helper.AddColumnToContentType(web, "EjemploTipoContenido", "Ejemplo1");
            Helper.AddColumnToContentType(web, "EjemploTipoContenido", "EjemploNumber1");
        }
    }
} 

Como podéis ver en el código, y porque no se extienda tanto éste como el post, los elementos se crean por medio de helpers cuyo contenido no he añadido, aunque si los queréis, puedo añadirlos más adelante.

De esta forma, estaremos eliminando esos problemas de dependencias que se crean, y además estaremos dando un paso hacia la forma de trabajar en el nuevo modelo de desarrollo de SharePoint.

Captura de pantalla 2015-04-19 a las 13.33.50

La misma técnica podríamos usar para, por ejemplo, crear listas y bibliotecas, haciéndolo programáticamente en lugar de usando el despliegue por medio de ficheros XML. La línea a seguir, debería ser, en la medida de lo posible, evitar los ficheros XML para desplegar elementos y reemplazarlos por el uso de la API de SharePoint, lo que se acercará más al uso de las APIs de cliente en apps que nos proponen como nuevo modelo de desarrollo.

Os dejo algunos enlaces interesantes sobre el tema y que os pueden ayudar:

http://www.microsoftvirtualacademy.com/training-courses/transform-sharepoint-customizations-to-sharepoint-app-model

https://github.com/OfficeDev/PnP

En próximas entradas os contaré algunas cosillas más sobre el nuevo modelo de desarrollo y hacia dónde deberíamos de irnos enfocando cuando nos enfrentamos a una solución basada en SharePoint que requiere extender la plataforma.

Un saludo a todos.

Anuncios

Accediendo a los datos de SharePoint OnLine a través de CSOM

Hola a todos,

Hoy quiero seguir avanzando en el conocimiento de las distintas APIs que tenemos disponibles para acceder a SharePoint. Hasta ahora, en entradas anteriores, había hablado sobre el uso de la API Javascript y sobre todo de la API REST.

Introducción a la API REST de SharePoint 2013

Creando una app para SharePoint OnLine

Con estas APIs se pueden desarrollar SharePoint Hosted Apps, alojadas en SharePoint, que se desarrollan con HTML5 + CSS3 y Javascript principalmente. Pero además, podemos crear otro tipo de aplicaciones, que no están alojadas en SharePoint como las anteriores y que están alojadas en un sitio externo a éste, como puede ser un sitio de Azure. Las SharePoint Provider Apps pueden ser proyectos ASP.NET como WebForms o MVC. Para acceder a SharePoint desde este tipo de aplicaciones tenemos disponible la API CSOM (Client Side Object Model). Esta API es C#, lo que a priori, para los que hemos desarrollado habitualmente en el modelo de servidor puede suponer una ventaja.

El objetivo de esta entrada es hacer una pequeña introducción de cómo tenemos que proceder para realizar algunas de las operaciones básicas que pueden sernos útiles a la hora de trabajar con SharePoint.

Estos ejemplos se han basado en la creación de una Provider Hosted App con una aplicación MVC. Dicha aplicación, tiene permisos para manejar la colección de sitios, concedidos a través del AppManifest.xml.

Accediendo a listas

El código que vamos a usar para acceder a una lista por medio de la API CSOM es el siguiente:

public class NewsModel
{
    public string Title { get; set; }
    public string Cuerpo { get; set; }
    public string ID { get; set; }
    public DateTime Created { get; set; }
}
public void SeeList(string listname)
{
      var spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext);

      using (var clientContext = spContext.CreateUserClientContextForSPHost())
      {
           if (clientContext != null)
           {
                List news = clientContext.Web.Lists.GetByTitle(listname);

                CamlQuery query = new CamlQuery();
                query.ViewXml = "<View><Query><OrderBy><FieldRef Name='ID' Ascending='False' /></OrderBy></Query><ViewFields><FieldRef Name='Title' /></ViewFields><QueryOptions /><RowLimit>2</RowLimit></View>";
                    
                ListItemCollection items = news.GetItems(query);

                clientContext.Load(items);
                clientContext.ExecuteQuery();

                List<NewsModel> list = new List<NewsModel>();

                foreach(ListItem item in items)
                {
                    NewsModel newNews = new NewsModel
                    {
                         Title = item["Title"].ToString(),
                         ID = item.Id.ToString()
                    };

                    list.Add(newNews);
                 }

            }
        }
}

A la hora de hacer una consulta hay algunos aspectos que se debe de tener en cuenta:

  • Al ser una llamada desde el cliente, es importante intentar que las consultas sean lo más óptimas posibles, no olvidando establecer el RowLimit y las columnas que se quieren leer en la consulta, evitando leer información innecesaria.
  • Se pueden hacer todas las llamadas a métodos de la API que se quieran antes de llamar a la función clientContext.Load() y clientContext.ExecuteQuery(). Pero si lo que queremos es acceder a las propiedades de un elemento, es obligatorio previamente, hacer las llamadas a estos dos métodos del contexto para que se ejecute la consulta y la información esté ya cargada. En caso contrario, obtendremos una excepción de tipo NullReference.

Añadiendo elementos a una lista

Vamos a ver ahora cómo podríamos añadir un elemento en una lista por medio de CSOM. Para ello, se supone la existencia de una lista que tiene los campos Título,Body y Gist.

public void AddElementToList(string listname, string title, string body, string gist)
{
    var spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext);

    using (var clientContext = spContext.CreateUserClientContextForSPHost())
    {
          if (clientContext != null)
          {
                List examples = clientContext.Web.Lists.GetByTitle(listname);

                ListItemCreationInformation creationInformation = new ListItemCreationInformation();

                ListItem newItem = examples.AddItem(creationInformation);
                newItem["Title"] = title;
                newItem["Body"] = body;
                newItem["Gist"] = gist;

                newItem.Update();

                clientContext.ExecuteQuery();
           }
     }
}

Creando una lista

Vamos a ver por último, como crear una lista usando este modelo de objetos.La última función, creará una lista de tipo genérico y añadirá algunos campos a la misma.

public void CreateList(string ListName)
{
      var spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext);

      using (var clientContext = spContext.CreateUserClientContextForSPHost())
      {
          if (clientContext != null)
          {
              ListCreationInformation informationToList = new ListCreationInformation
              {
                   Title = ListName,
                   TemplateType = (int)ListTemplateType.GenericList,
                   Description = "This is a Client Created List"
              };

              List newList = clientContext.Web.Lists.Add(informationToList);

              newList.Fields.AddFieldAsXml("<Field DisplayName='Body' Type='Note' Required='True' />", true, AddFieldOptions.DefaultValue);
              newList.Fields.AddFieldAsXml("<Field DisplayName='Gist' Type='Text' Required='True' />", true, AddFieldOptions.DefaultValue);

              newList.Update();

              clientContext.ExecuteQuery();

           }
      }
}

Con estos ejemplos, espero que quede una ligera idea de, qué y cómo se puede trabajar con SharePoint y C# desde el modelo de objetos de cliente en SharePoint Provider Apps.

Este modelo de objetos tiene mucha relevancia dentro del modelo de provisionamiento remoto que está promoviendo Microsoft para sustituir al tradicional modelo de desarrollo basado en el Feature Framework por el nuevo modelo de desarrollo basado en apps. Aunque de este tema os hablaré más adelante.

Y nada más por hoy, espero que como siempre os sea de utilidad.