En el artículo Consultando listas con WCF Data Services veíamos como podíamos utilizar el nuevo servicio ODATA para consultar y actualizar los datos de las listas de SharePoint. Hoy nos toca explicar la nueva API manejada de cliente Microsoft.SharePoint.Client.
Esta nueva API, además de ofrecernos la capacidad de realizar consultas y actualizaciones sobre las listas, nos permite interactuar con los objetos más comunes de SharePoint. Con los WCF Data Services sólo podemos consultar y actualizar los elementos de las listas, mientras que con esta API podremos crear y eliminar listas, crear, actualizar y eliminar elementos de las listas, modificar documentos de las librerías, crear sitios, administrar permisos, administrar web parts, etc.
Estas funcionalidades las tenemos presentes en los servicios web de SharePoint, pero no es un secreto que estos servicios web son algo engorrosos y complicados de desarrollar. Normalmente desarrollamos nuestros servicios web que, utilizando el API de servidor, expone las funcionalidades que necesitábamos, pero con una interfaz más amigable y programable.
Tenemos tres posibles clientes para este API:
-
Código Manejado .NET
-
Silverlight
-
ECMAScript
Escribir código con la API de cliente es muy similar a escribir con el API de servidor, salvo por algunas especificaciones que iremos viendo.
Hablemos del modelo
Cuando nuestra aplicación utiliza el API para realizar consultas sobre SharePoint, el API genera un XML que es enviado al servidor, el servidor recibe esa petición, procesa las peticiones utilizando el modelo de objetos del servidor y devuelve al API la respuesta en formato JSON. El API de cliente analiza el JSON recibido y le entrega al código los objetos con los datos solicitados (objetos .NET u objetos JavaScript).
Para mejorar el rendimiento de las peticiones al servidor, el API necesita de la llamada a un método para que se realizan todos los procesos especificados. Esto es, podemos realizar múltiples procedimientos (actualizar elementos de lista, crear lista, etc) y estos no se ejecutarán en el servidor hasta que se lo especifiquemos con la llamada al método ExecuteQuery.
Vamos a un ejemplo y así comprenderemos esta funcionalidad del API.
Utilizando el API
Lo primero que tenemos que hacer es agregar en nuestro proyecto las referencias a dos ensamblados de SharePoint, Microsoft.SharePoint.Client.dll y Microsoft.SharePoint.Client.Runtime.dll (de momento los podemos encontrar en nuestro servidor en la carpeta %ProgramFiles%Common FilesMicrosoft SharedWeb Server Extensions14ISAPI, en un futuro me imagino que habrá algún instalador para instalar estas API en un entorno de desarrollo que no tenga SharePoint).
Para consultar un sitio, ejecutamos el siguiente código:
1: ClientContext context = new ClientContext("http://intranet.contoso.com");
2: Web site = context.Web;
3: context.Load(site);
4: context.ExecuteQuery();
5: Console.WriteLine("Título: {0}", site.Title);
Lo primero que tenemos que hacer es instanciar un ClientContext con la url de nuestro sitio, este es el encargado de realizar las llamadas XML y recibir el JSON de nuestro servidor. Luego nos creamos una variable para recibir los valores de nuestro sitio, le especificamos al contexto que realice la carga de éste y en la llamada a ExecuteQuery es cuando se envía y se recibe la información del servidor.
Es importante tener en cuanta la llamada Load con la variable de nuestro site. Esta llamada especifica al contexto que realice la carga de esta tipo de variable, sino lo especificáramos, la variable site estaría vacía y no tendría la información que estábamos esperando. Resumiendo:
-
Especificamos al contexto las operaciones que tiene que enviar al servidor. Esto incluye acceso a datos de listas, sitios, web parts, consultas CAML, objetos a crear, modificar, eliminar, etc.
-
Luego realizamos la llamada a ExecuteQuery, que envía la petición XML al servidor y recibe los objetos JSON con los datos solicitados.
Siguiendo con este modelo de desarrollo, veamos como podemos crear una lista e insertar elementos en ella, para esto necesitamos hacer uso de dos clases ListCreationInformation y ListItemCreationInformation que nos permite especificar los valores específicos de cada elemento. Por ejemplo, para la creación de la lista le especificamos el Title, TemplateType, etc, mientras que para cada elemento lo único que necesitamos es crear un nuevo Item en la lista y ejecutar el Update cuando hayamos terminado de crearlo.
1: ClientContext context = new ClientContext("http://intranet.contoso.com");
2: Web site = context.Web;
3:
4: //Creamos una lista de Tasks
5: ListCreationInformation listCreationInformation = new ListCreationInformation();
6: listCreationInformation.Title = "Tareas";
7: listCreationInformation.TemplateType = (int)ListTemplateType.Tasks;
8: List list = site.Lists.Add(listCreationInformation);
9:
10: //Creamos elemento en la lista de Tasks
11: ListItemCreationInformation listItemCreationInformation = new ListItemCreationInformation();
12:
13: ListItem item = list.AddItem(listItemCreationInformation);
14:
15: item["Title"] = "Titulo tarea 1 desde API";
16: item["Body"] = "Descirpcion de la tarea 1";
17: item.Update();
18:
19: item = list.AddItem(listItemCreationInformation);
20: item["Title"] = "Titulo tarea 1 desde API";
21: item["Body"] = "Descirpcion de la tarea 1";
22: item.Update();
23:
24: context.ExecuteQuery();
y como se realizan consultas sobre las listas:
1: ClientContext context = new ClientContext("http://intranet.contoso.com");
2:
3: List list = context.Web.Lists.GetByTitle("Tasks");
4:
5: CamlQuery camlQuery = new CamlQuery();
6: camlQuery.ViewXml = "<View/>";
7: ListItemCollection listItems = list.GetItems(camlQuery);
8:
9: context.Load(list);
10: context.Load(listItems);
11: context.ExecuteQuery();
12:
13: foreach (ListItem listItem in listItems)
14: Console.WriteLine("Id: {0} Titulo: {1}", listItem.Id, listItem["Title"]);
Primero obtenemos la lista por nombre (Lists.GetByTitle), creamos un CalmQuery y obtenemos los elementos de la lista con list.GetItems(CalmQuery). Le especificamos al contexto que realice la carga de la lista y de los elementos de la lista y ejecutamos la consulta.
Si pensamos en las listas que los usuarios crean en SharePoint, cabe la posibilidad de tener listas con gran cantidad de campos y nuestra aplicación sólo necesita 2 o 3 para realizar un proceso. Vemos como realizar una consulta de una lista especificando que nos devuelve sólo aquellos campos que necesitamos.
1: ClientContext context = new ClientContext("http://intranet.contoso.com");
2:
3: List list = context.Web.Lists.GetByTitle("Tasks");
4: CamlQuery camlQuery = new CamlQuery();
5: camlQuery.ViewXml =
6: @"<View>
7: <Query>
8: <Where>
9: <Eq>
10: <FieldRef Name='Status'/>
11: <Value Type='Text'>Completed</Value>
12: </Eq>
13: </Where>
14: </Query>
15: <RowLimit>100</RowLimit>
16: </View>";
17:
18: ListItemCollection listItems = list.GetItems(camlQuery);
19: context.Load(
20: listItems,
21: items => items
22: .Include(
23: item => item["Title"],
24: item => item["Assigned To"],
25: item => item["Completed"]));
26:
27: context.ExecuteQuery();
28:
29: foreach (ListItem listItem in listItems)
30: {
31: Console.WriteLine("Titulo: {0}", listItem["Title"]);
32: Console.WriteLine("Categoria: {0}", listItem["Assigned To"]);
33: Console.WriteLine("Completado: {0}", listItem["Completed"]);
34: Console.WriteLine();
35: }
En el ejemplo anterior, obtenemos una lista por nombre, creamos un CamlQuery para realizar una consulta a esa lista y cargamos los elementos de la lista especificándole, con la expresión Lambda Include, los campos que queremos que nos devuelvan.
Ahora vamos a ver como se pueden realizar actualizaciones de los datos de la lista que hemos obtenido.
1: foreach (ListItem listItem in listItems)
2: {
3: listItem["Title"] = "Nuevo titulo";
4: listItem.Update();
5: }
6:
7: context.ExecuteQuery();
Para cada elemento de la lista, realizamos los cambios necesarios y ejecutamos el método Update para que el contexto sepa que tiene que realizar la actualización de ese elemento y especifique el proceso en la respuesta XML que envía al servidor.
Todo esto y mucho más es lo que podemos hacer con esta API, incluso podemos recorrer los campos de una lista y generar el xml con los campos que la componen. También es interesante conocer la posibilidad de realizar consultas limitando el número de elementos que recibimos, o incluso paginando las consultas recibiendo los elementos paginados.
1: ListItemCollectionPosition itemPosition = null;
2: while (true)
3: {
4: CamlQuery camlQuery = new CamlQuery();
5:
6: camlQuery.ListItemCollectionPosition = itemPosition;
7: camlQuery.ViewXml =
8: @"<View>
9: <ViewFields>
10: <FieldRef Name='Title'/>
11: <FieldRef Name='AssignedTo'/>
12: <FieldRef Name='Status'/>
13: </ViewFields>
14: <RowLimit>10</RowLimit>
15: </View>";
16:
17: ListItemCollection listItems = list.GetItems(camlQuery);
18: context.Load(listItems);
19: context.ExecuteQuery();
20: itemPosition = listItems.ListItemCollectionPosition;
21:
22: foreach (ListItem listItem in listItems)
23: Console.WriteLine(" Titulo: {0}", listItem["Title"]);
24:
25: if (itemPosition == null)
26: break;
27:
28: Console.WriteLine(itemPosition.PagingInfo);
29: Console.WriteLine();
30: }
Vemos como se especifica un límite de 10 elementos con RowLimit en el CamlQuery y se utiliza la clase ListItemCollectionPosition para mantener el cursor en la página de elementos que tenemos actualmente en memoria.
Esta API es un gran avance que nos permite crear código de cliente para interactuar con nuestro queridísimo SharePoint, por ejemplo, para crear documentos Words en memoria con Open XML y almacenarlos en una Biblioteca de Documentos. Si, lo sé, antes también podíamos hacerlo, pero los afortunados como yo que hemos utilizado los servicios web de SharePoint, siempre nos acordamos del equipo de desarrollo que lo pensó.
Saludos a todos…
Recién estrenado el mes de marzo, aquí os dejo una nueva entrega del recopilatorio de recursos