Todas las entradas de: Alberto Diaz Martin

Head of Innovation y Principal Team Leader en Encamina. Su labor en Encamina es liderar el desarrollo de soluciones Microsoft aportando el necesario perfil de innovación que se necesita en las empresas. Para la comunidad, ha fundado TenerifeDev (www.tenerifedev.com) con otros colaboradores, un grupo de usuarios de .NET en Tenerife, y es coordinador de SUGES (Grupo de Usuarios de SharePoint de España, www.suges.es) y colaborador con otras comunidades de usuarios. Un asiduo conferenciante en webcast y conferencias de tecnología de habla hispana. Desde el año 2013, ha iniciado una nueva etapa como Co-Director de la revista especializada en SharePoint, CompartiMOSS Microsoft MVP Office Servers and Services desde el año 2011 (http://mvp.microsoft.com/es-es/mvp/Alberto%20Diaz%20Martin-4030767)

Widgets en Windows Mobile

En Windows Mobile 6.5 se pueden desarrollar aplicaciones basadas en el estándar W3C Widgets. Esto nos permite empaquetar aplicaciones HTML y JavaScript y que se ejecuten en nuestros dispositivos móviles.

Utilizando HTML y CSS proporcionamos la interfaz de usuario, y el código lo escribiremos en JavaScript. Los Widgets se instalan localmente en el dispositivo móvil y se ejecutan de forma local, pero pueden utilizar datos de la nube.

La pregunta es, ¿Por qué desarrollar un widget para un dispositivo móvil que obtiene datos de la nube, si sería los mismo, incluso más rápido, desarrollar una aplicación web a la que acceda el dispositivo cuando necesite estos datos?

Los widgets nos permiten ahorrar tráfico en el dispositivo. Cuando un dispositivo móvil accede a una aplicación web se tiene que descargar el html, las css, las imágenes, los archivos javascript, y, además de todo esto, obtener la información que le solicitamos. Si analizamos la arquitectura de un widget, este está pensado para que nuestro dispositivo se centre en obtener los datos y no en obtener la capa de presentación del mismo. Esto quiere decir, nuestros widgets consumen menos tráfico de red que las aplicaciones web, y esta es una característica de diseño clave para nuestros dispositivos móviles que están basado en las redes que nos cobran por la cantidad y la velocidad de descarga de paquetes. Si a todo esto le sumamos que existe un widget API que nos proporciona objetos JavaScript que nos permite integrar nuestras aplicaciones con el dispositivo (funciones de localización, estado del dispositivo, acceso a menús, etc.), encontramos dos grandes ventajas para desarrollar widgets y no aplicaciones web.

Contenido del Paquete

Un widget es un fichero Zip con el contenido de nuestra aplicación web. Páginas HTML, código JavaScript, hojas de estilos CSS, imágenes, etc.

Widget_1

Dentro de este zip, debemos de incluir un fichero config.xml, que es el Manifest del widget. Todos los elementos del config son opcionales, pero es recomendable incluirlos para conocer la información clave de nuestro widget.

   1: <?xml version="1.0" encoding="utf-8" ?>

   2: <widget xmlns="http://www.w3.org/ns/widgets"

   3:         version="1.0"

   4:         id="http://someSite.com/MyUniqueWidgetID">

   5:   <name>My first widget</name>

   6:   <content src="widget.htm" type="text/html" />

   7:   <access network="true" />

   8:   <icon src="icon.png"/>

   9:   <icon src="icon.ico"/>

  10:   <description>

  11:     This is my first widget,

  12:     it won't make a lot of money on the

  13:     marketplace but at least is cute!

  14:   </description>

  15:   <author href="http://www.myweb.com"

  16:           email="widget@myweb.com">Authos Name</author>

  17:   <license>

  18:     Example license

  19:   </license>

  20: </widget>

Los elemento del config son:

  • Version. Número de versión del widget, para poder realizar las actualizaciones utilizando el widget id.
  • Id. Este elemento es el que va a identificar a nuestro widget en el dispositivo. Tiene que ser una URI, pero no tiene que ser valida o accesible.
  • Name, Description, Author, License. Estos campos describen la información del widget basada en nombre, descripción de la funcionalidad, autor del mismo, licencia.
  • Content. Este elemento especifica que fichero se va a cargar cuando el widget se inicie.
  • Icon. Nos permite especificar el icono que va a mostrar nuesto widget en el menú de nuestro dispositivo móvil.
  • Access Network. Nos sirve para especificar si nuestro widget tendrá permisos para acceder a los recursos de la red.

Por su puesto, toda esta información es accesible para nuestra aplicación utilizando el widget API.

Programando el Paquete

Vamos a desarrollar un widget que, utilizando jQuery y el API de Twitter, nos muestre los mensajes de un usuario.

Widget_2

Esta es nuestra estructura, ahora explicaremos que es lo que tenemos en ella.

config.xml

Definimos nuestro fichero Manifest de la siguiente forma:

   1: <?xml version="1.0" encoding="utf-8" ?> 

   2: <widget version="1.0" 

   3:         xmlns="http://www.w3.org/ns/widgets" 

   4:         id="http://adiaz.ms/Twitter"> 

   5:   <name>Twitter Search</name> 

   6:   <content src="inicio.html" type="text/html" /> 

   7:   <access network="true" /> 

   8:   <icon src="img/icon.png"/> 

   9:   <description>Este widget realiza consultas de las publicaciones de un usuario de Twitter.</description> 

  10: </widget> 

Incluimos los siguiente ficheros: jquery.min.js y jquery.twitter.js. Estos dos ficheros JavaScript nos permiten realizar consultas al API de Twitter y mostrar los mensajes de un usuario.

Una vez que tenemos todos estos, incluyendo hojas de estilos y alguna que otra imagen, escribimos la página html que va a contener la funcionalidad de obtener los mensajes de un usuario de Twitter.

   1: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

   2: <html xmlns="http://www.w3.org/1999/xhtml">

   3:     <head>

   4:         <title>Search on Twitter</title>

   5:         <link rel="stylesheet" href="css/jquery.twitter.css" type="text/css" media="all" />

   6:         <script type="text/javascript" src="js/widget.js"></script>
   1:  

   2:         <script type="text/javascript" src="js/jquery.min.js">

   1: </script>

   2:         <script type="text/javascript" src="js/jquery.twitter.js">

   1: </script>

   2:         <script type="text/javascript" src="js/twitter.js">

   1: </script>

   2:         <script type="text/javascript">applyCssByDPI();

</script>

   7:     </head>

   8:  

   9:     <body onload="SetupPage()">

  10:         <form id="search_form" action="javascript:SearchTwitter();" >

  11:             <div id="content">

  12:                 <div class="sw_b1 sw_qbox" id="search_box">

  13:                     <table width="100%"><tr>

  14:                         <td id="search_box_cell">

  15:                             <input class="sw_qbox" id="q" name="q" title="Search for..." type="text" value="" maxlength="48" />

  16:                         </td>

  17:                         <td align="right">

  18:                             <input class="sw_qbtn" id="go" name="go" tabindex="0" title="Search" type="button" onclick="javascript:SearchTwitter();"/>

  19:                         </td>

  20:                     </tr></table>

  21:                     <input name="a" id="a" type="hidden" value="results" />

  22:                     <input name="mid" id="mid" type="hidden" value="1017" />

  23:                 </div>

  24:                 <div id="twitter"></div>

  25:             </div>

  26:         </form>

  27:     </body>

  28: </html>

 

 

En esta página se hace uso del API de widgets para dos funciones básicas, una para seleccionar el CSS en función del tamaño del dispositivo y otra para configurar el menú de nuestro widget para que nos salga el menú Buscar en las opciones del mismo.

Selección del CSS

 

   1: function applyCssByDPI() {

   2:     if (WidgetAPI.getDeviceDPI() == WidgetAPI.WIDGET_DEVICE_HIGH_DPI)

   3:         document.write('<link rel="stylesheet" type="text/css" href="css/twitter192.css" />');

   4:     else {

   5:         document.write('<link rel="stylesheet" type="text/css" href="css/twitter.css" />')

   6:     }

   7: }

Creación del Menú

   1: function SetupPage() {

   2:     setLeftSoftKey(SEARCH_MENU_ITEM_ID);

   3: }

   4:  

   5: var SEARCH_MENU_ITEM_ID = 1005;

   6:     

   7: function menuHandler(a) {

   8:     switch (a) {

   9:         case SEARCH_MENU_ITEM_ID:

  10:             document.getElementById("search_form").submit()

  11:     }

  12: }

  13:  

  14: function setLeftSoftKey(d, c) {

  15:     var a = null;

  16:     if (c)

  17:         a = c;

  18:     else

  19:         a = menuHandler;

  20:     var b = null;

  21:     switch (d) {

  22:         case SEARCH_MENU_ITEM_ID:

  23:             b = WidgetAPI.createMenuItem(SEARCH_MENU_ITEM_ID, "Buscar", a);

  24:             break;

  25:     }

  26:     if (b)

  27:         WidgetAPI.setLeftSoftKey(b)

  28: }

 

Y con todo esto nos queda empaquetar en un Zip nuestros ficheros, cambiarle la extensión por .widget, copiarlo a nuestro dispositivo móvil, instalarlo y ejecutarlo para ver que es capaz de obtener los mensajes de un usuario de Twitter, utilizando Ajax.

Podemos ver los widgets instalados en la carpeta Dispositivo Móvil/Archivos de Programa/Widgets/User/ID (este Id es el que el dispositivo móvil le pone cuando se instala).

Widget_3

En la MSDN hay un muy buen artículo explicando en detalle como Desarrollar Widgets para Windows Mobile 6.5.

A ver si consigo otros móviles (Android, BlackBerry, etc) y escribo un artículo con los cambios que tendríamos que realizar para conseguir la multiplataforma de nuestro widgets.

Os dejo en enlace al widget de ejemplo que he utilizado en el artículo.

 

Saludos a todos…

[SecondNug] Webcast Modelando entidades, Entity Framework 4

El próximo martes 16 de Marzo a las 18.30 (GMT) me toca estrenarme en SecondNug con un evento en el que intentaré enseñaros a pensar como el nuevo Entity Framework 4.0.

banner_EF4

Hablaré de las nuevas capacidades de la versión 4 para poder utilizarlo en múltiples enfoques de desarrollo, por ejemplo, el uso de code-first, model-first y database-first. Todo esto, manteniendo independiente nuestra Interfaz de usuario (WPF, Silverlight, WCF, ASP.NET, etc.) del modelo de acceso a datos. Intentaré explicaros las ventajas y desventajas de trabajar con Entity Framework y veremos las novedades que nos vienen con esta nueva versión.

Os espero a todos!

Enlace de registro del evento: http://msevents.microsoft.com/CUI/WebCastEventDetails.aspx?EventID=1032444597&EventCategory=4&culture=es-AR&CountryCode=AR

No olvidéis tener instalado Microsoft Office Live Meeting (lo podemos descargar en el siguiente enlace).

Hazte Extensible con MEF

[UPDATE] Os podéis descargar el código de ejemplo

En la nueva versión del Framework (4.0) tendremos una librería que nos permitirá crear aplicaciones extensibles con cierta facilidad. Managed Extensibility Framework (MEF) permite que nuestras aplicaciones sean capaces de ofrecer nuevas funcionalidades sin necesidad de recompilar el código, tan solo añadiendo la DLL extensible como si de un Plug-in se tratase.

mef-blocks

¿Cómo funciona MEF?

MEF se basa en un Catálogo y en un CompositeContainer. El catálogo es el encargado de realizar el descubrimiento de las extensiones y el contenedor se encarga de descubrir y cargar las extensiones y satisfacer las dependencias.

Las partes extensibles deben de cumplir un contrato o interfaz y son cargadas en nuestra aplicación a través del catálogo. Veamos un ejemplo:

Definamos un contrato para el envío de una lista de ficheros a diferentes destinos (ftp, http, etc.)

   1: /// <summary>

   2: /// Contrato para el envío de una lista de ficheros.

   3: /// </summary>

   4: public interface ISend

   5: {

   6:     /// <summary>

   7:     /// Nombre del modo de envío

   8:     /// </summary>

   9:     string Name { get; }

  10:  

  11:     /// <summary>

  12:     /// Método que realiza el envío de la lista de ficheros

  13:     /// </summary>

  14:     /// <param name="files">Lista de ficheros a enviar</param>

  15:     /// <returns>Devuelve True si se ha realizado el envío correctamente.</returns>

  16:     bool AllFiles(List<string> files);

  17: }

Este contrato es el que tendrán que cumplir las extensiones que podemos añadir a nuestra aplicación.

El siguiente paso es realizar la carga de las extensiones para tenerlas disponibles para su uso.

   1: [Import]

   2: public ISend Send { get; set; }

   3:  

   4: private void Compose()

   5: {

   6:     var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());

   7:     var container = new CompositionContainer(catalog);

   8:     container.ComposeParts(this);

   9: }

Instanciamos el Catálogo de nuestro ensamblado y realizamos la carga de las extensiones con el CompositionContainer. Cuanto se carga la extensión, quedará disponible para el uso en la propiedad definida por el atributo Import.

Otra posibilidad, a parte de la de crear nuestros propios catálogos, es utilizar el System.ComponentModel.Composition.Hosting.DirectoryCatalog. Esta catálogo nos permite realizar la carga de las extensiones desde un directorio de la siguiente forma:

   1: [ImportMany("send", typeof(ISend))]

   2: public IEnumerable<ISend> Sends { get; set; }

   3:  

   4: private void Compose()

   5: {

   6:     string path = Assembly.GetExecutingAssembly().CodeBase.Replace("file:///", string.Empty).Substring(0, path.LastIndexOf(@"/"));

   7:  

   8:     var catalog = new DirectoryCatalog(path + @"/Extensions/");

   9:     var contanier = new CompositionContainer(catalog);

  10:     contanier.ComposeParts(this);

  11: }

Como vemos en el código, hemos cambiado el Import por ImportMany para que sea capaz de cargar todos los extensores que se encuentren en el directorio que le vamos a especificar. Luego, en vez de utilizar el AssemblyCatalog, cargamos los extensores del directorio Extensions con el DirectoryCatalog. Esto realizará la carga del Enumerable de ISend y podremos seleccionar uno de ellos o todos para utilizar sus métodos.

Para ejecutar los métodos de un extensor sólo tendremos que obtenerlo de la propiedad definida con el Import (o del enumerado del ImportMany) y realizar la llamada al método que se ha definido en la interfaz.

   1: Send.AllFiles(ficheros);

   1: foreach (ISend item in Sends)

   2: {

   3:     item.AllFiles(ficheros);

   4: }

El contenedor es el encargado de instancias la extensión y permitir la ejecución del método requerido.

¿Cómo creamos extensiones?

Las extensiones no son más que ensamblados que implementan la interfaz del contrato, cumpliendo así con este y aplicando su propia lógica de programación.

   1: [Export("send", typeof(ISend))]

   2: public class Ftp : ISend

   3: {

   4:     string ftpAddress = "ftp.test.es";

   5:  

   6:     public string Name

   7:     {

   8:         get { return "FTP"; }

   9:     }

  10:  

  11:     public bool AllFiles(List<string> files)

  12:     {

  13:         FtpWebRequest ftpRequest = FtpWebRequest.Create(ftpAddress) as FtpWebRequest;

  14:         ftpRequest.Method = WebRequestMethods.Ftp.UploadFile;

  15:         ftpRequest.Credentials = System.Net.CredentialCache.DefaultCredentials;

  16:  

  17:         foreach (string file in files)

  18:         {

  19:             FileStream stream = File.OpenRead(file); 

  20:             byte[] buffer = new byte[stream.Length];

  21:             stream.Read(buffer, 0, buffer.Length); 

  22:             stream.Close();

  23:  

  24:             Stream reqStream = ftpRequest.GetRequestStream(); 

  25:             reqStream.Write(buffer, 0, buffer.Length); 

  26:             reqStream.Close();

  27:         }

  28:  

  29:         return false;

  30:     }

  31: }

Básicamente declaramos nuestra clase con el atributo Export y, opcionalmente, le especificamos de que tipo es. Implementamos los métodos que sean requeridos por la interfaz y listo. Compilamos este ensamblado, lo ponemos en el directorio que hemos indicado para la carga de los extensores y listo. Ya tenemos accesible esta extensión para el envío de ficheros por ftp. El resto de extensiones se desarrollan de la misma manera y se colocan en ensamblados por separado en la carpeta de las extensiones.

Fíjense que nuestra interfaz tiene un método llamado Name que podemos utilizar para realizar consultas LINQ y obtener el extensor que queremos, consultando por nombre.

   1: ISend extensor = Sends.Where(s => s.Name == "FTP").FirstOrDefault();

   2: extensor.AllFiles(ficheros);

Con MEF tenemos un Framework sencillo que nos permite añadirle mucha potencia a nuestras aplicaciones. Con pocas líneas de código hemos sido capaces de añadir diversas funcionalidades a nuestra aplicación sin tener que recompilarla y instalarla en los clientes. Por supuesto, esto es un pequeño ejemplo de la potencia de MEF, ahora nos quedaría poder personalizar el catálogo para que un usuario pueda elegir que extensores quiere, poder configurar estos extensores, etc.

 

Saludos a todos…

Más rápido con Visual Studio 2010. Depuración colaborativa

Cuantas veces le hemos enviado un bug a otro desarrollador y no poder volver reproducirlo. El equipo de Visual Studio nos quiere ayudar con estos problemillas. Una de las nuevas funcionalidades que tenemos es la posibilidad de crear depuraciones en colaboración. Os pongo un ejemplo y lo explico.

Cuando el Desarrollador 1 le envía el error al Desarrollador 2, se pierde mucha información que el Desarrollador 1 ya ha investigado y que el Desarrollador 2 tiene que volver a encontrar e investigar.

Veamos cómo podemos mejorar este proceso utilizando Visual Studio 2010. El Desarrollador 1 depurando su código encuentra que puede existir un error en la llamada a un método de un componente del Desarrollador 2.

En este punto, el Desarrollador 1 debería de enviar el error al Desarrollador 2, pero en vez de enviárselo y que el Desarrollador 2 tenga que volver a realizar la depuración, generará una serie de información de depuración que se le envía al Desarrollador 2 con el error. El Desarrollador 1 comparte su entorno de depuración con el Desarrollador 2. Lo primero de todo es exportar el breakpoint actual para que el Desarrollador 2 sepa dónde tiene que empezar la depuración del error.

Lo siguiente es compartir la información que el Desarrollador 2 debe de inspeccionar al depurar. Para hacer esto, primero debemos anclar la variable que causa el error y exportar esos datos de depuración. Incluso podemos añadirle comentarios a la variable para que lo lea el Desarrollador 2.

Ahora, el Desarrollador 2 puede continuar con la depuración del Desarrollador 1 importando la información de depuración que se le ha enviado.

Cuando el Desarrollador 2 importa el entorno de depuración, puede continuar con el mismo, incluso con los valores de las variables y los comentarios que se han exportado.

Este proceso de Depuración Colaborativa nos permite mejorar la integración del equipo de Testers y que estos sean capaces de enviar errores e información de calidad para poder reproducirlos y corregirlos. Aunque no hayamos hablado de él, tenemos que tener en cuenta el nuevo proceso de depuración de Visual Studio 2010 llamado Intellitrace.

Intellitrace nos permite mantener un histórico de nuestra depuración para poder navegar hacia atrás en nuestro código y visualizar el estado de este. Espero poder escribir un artículo en breve sobre él explicando su uso y sus ventajas (por ejemplo, si estamos probando nuestra aplicación y se produce una excepción, no tenemos que poner un breakpoint y volver a empezar hasta llegar a él para depurar el error, si tenemos habilitado Intellitrace automáticamente podremos depurar la excepción cuando esta se produzca).

 

Saludos a todos…

SharePoint 2010. Client API

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…

SharePoint 2010. Personalizando formularios con InfoPath

Una de las nuevas características de SharePoint 2010, es la capacidad de poder personalizar los formularios de las listas (New Form, Edit Form and View Form) con InfoPath 2010.

Con la versión actual (MOSS 2007) tenemos la posibilidad de personalizar los formularios de nuestros Workflows con InfoPath, podíamos tener una biblioteca de formularios en la que los usuarios rellenan los datos de una lista utilizando un documento InfoPath, pero no tenemos la posibilidad que nos ofrece SharePoint 2010 en la que un usuario, con conocimientos de InfoPath, puedo con no más de dos clicks de ratón personalizar el formulario de la una lista.

Os lo muestro con un ejemplo, que será más fácil ver las diferencias.

Vamos a crearnos una lista de Contacts en SharePoint 2010 para personalizar el formulario. Para quien no conozca de InfoPath, comentarles que es una aplicación para desarrollar formularios basados en XML, incluyendo validadores de campos, consultas a orígenes de datos externos, formateo de campos, etc.

SharePoint2010_InfoPathCustomForm1

En la pestaña de List, tenemos una opción con el icono de InfoPath llamada Customize Form. Cuando seleccionamos esta opción, se nos abrirá InfoPath 2010, con un formulario que se ha creado con los elementos de la lista de Contacts.

SharePoint2010_InfoPathCustomForm2

Validación de campos con reglas de InfoPath

Las listas de SharePoint 2010 permiten la validación de los campos utilizando formulas, estas validaciones son del tipo comparación (con valores u otros campos), sin embargo, no podemos realizar validaciones con expresiones regulares. InfoPath 2010, además de este tipo de validaciones, incluye algunas algunas más, como las de expresiones regulares. Por ejemplo, en el formulario de Contacts, vamos a poner una validador para que el usuario tenga que escribir un email correctamente.

SharePoint2010_InfoPathCustomForm3

InfoPath incluye algunas expresiones regulares comunes (email, código postal, url), pero también podemos escribir nuestras expresiones para realizar la validación.

SharePoint2010_InfoPathCustomForm4

Estas reglas pueden ser de Validación, Formateo o Acción. Para nuestro ejemplo, utilizaremos una regla de validación.

SharePoint2010_InfoPathCustomForm5

También vamos a crear una regla de Formateo para que el campo Company se ponga en amarillo cuando se escriba una empresa llamada Contoso.

SharePoint2010_InfoPathCustomForm6

Filtrar Lookups

También podemos realizar filtros de los campos Lookups de la lista. Por ejemplo, hemos modificado la lista de Contacts, para que el Job Title sea un campo Lookup. Vamos a ver como podemos filtrar ese campo para que sólo salgan los Job Title de Internal Use.

En las propiedades del Drop-Down List del campo Job Title, vemos que está conectado a un origen de datos externos, llamado Job Title y en el Entries se ha seleccionado todos los elementos de la lista de SharePoint.

SharePoint2010_InfoPathCustomForm7

Para filtrar, tenemos que cambiar el Entries para que no nos devuelva toda la lista de elementos, si no que nos devuelva los elementos que tengan un ‘FALSE’ en el campo Internal use. Si nuestro origen de datos a filtrar no incluye este campo, podemos añadir una nueva conexión a una lista de SharePoint e incluir este campo.

SharePoint2010_InfoPathCustomForm8

Ahora nuestro Entries contiene la consulta filtrada (/dfs:myFields/dfs:dataFields/d:SharePointListItem_RW[d:Internal_x0020_use = string(false())]).

Nos queda publicar el formulario a la lista de SharePoint y listo. Hemos personalizado el formulario de la lista, sin necesidad de programación.

SharePoint2010_InfoPathCustomForm9

Ahora hemos pasado de tener un formulario estándar como este…

SharePoint2010_InfoPathCustomForm

a este otro con las reglas de InfoPath, el Lookup filtrado y algún cambio en el diseño…

SharePoint2010_InfoPathCustomForm10

Uno de los valores que siempre he defendido de SharePoint, es que, dentro de las posibilidades, ofrezca variedad en el Self-Service. Que los usuarios de SharePoint puedan crear listas, añadir campos, les ofrece muchas posibilidades. Ahora con esta nueva versión, hemos ampliado el menú del Self-Service y los usuarios podrán crear más elementos, más profesionales y sin necesidad de programación.

 

Saludos a todos…

SharePoint 2010. Consultando listas con WCF Data Services

Hoy vamos a empezar una serie de artículos que hablarán sobre los métodos que SharePoint 2010 nos ofrece para acceder a los elementos (listas, documentos, etc.) que almacena desde un cliente externo al servidor.

En la versión 2007 de SharePoint existen unos servicios web que nos permitían realizar consultas sobre las listas. Trabajar con estos servicios web complicaban un poco el desarrollo, ya que no sólo teníamos que conocer el modelo que íbamos a consultar, sino saber construir un lenguaje llamado CAML que nos permitía realizar consultas sobre estos datos.Al final, terminábamos desarrollando un servicio web que utilizando el API de servidor de SharePoint devolvía los datos de las listas con un formato un poco más amigable.

La nueva versión 2010 nos trae alguna novedades. Tenemos nueva API Microsoft.SharePoint.Client y un nuevo servicio que implementa Open Data Protocol usando WCF Data Services. Hoy nos centraremos en este último.

WCF Data Services en SharePoint 2010 ofrece las funcionalidades de la programación de cliente con Data Services, a través del servicio con la url http://<site>/_vti_bin/listdata.svc.

Si accedemos al servicio con un explorador web, obtendremos el Data Service Atom feed con todas las listas del sitio.

SharePoint2010_wcf1

Sin queremos consultar la lista Tasks, con poner la url del servicio seguida del nombre de la lista tenemos (http://<site>/_vti_bin/listdata.svc/Tasks), o incluso podemos obtener el xml de un elemento (http://<site>/_vti_bin/listdata.svc/Tasks(1)). También se incluyen todas las opciones de filtrado (http://<site>/_vti_bin/listdata.svc/Tasks?$filter=Description eq ‘Tarea 1’), ordenación (http://<site>/_vti_bin/listdata.svc/Tasks$orderby=Description desc) y etc.

SharePoint2010_wcf2

Peor esta no es la idea, está bien poder hacer consultas con el navegador, pero con un par de líneas de código podemos realizar consultas de este tipo o incluso de actualización de los elementos.

Abramos Visual Studio 2010 (o incluso Visual Studio 2008 SP1) y en un proyecto de consola agregamos la referencia de servicio a la url del mismo (http://<site>/_vti_bin/listdata.svc).

SharePoint2010_wcf3

La ventaja de utilizar WCF Data Services para consultar datos en SharePoint es que tendremos un Strongly Type DataContext y Strongly Type List (en el Servicio Web clásico de Sharepoint todas las listas son del mismo tipo y no tenemos diferencia entre ellas). WCF Data Services crea un modelo relacional de objetos para cada lista del sitio que estemos consultando.

Para realizar una consulta sobre la lista Tasks, sólo tendremos que instancia el HomeDataContext y realizar la consulta con LINQ.

   1: Uri intranetUri = new Uri("http://intranet.contoso.com/_vti_bin/listdata.svc", UriKind.Absolute);

   2:  

   3: IntranetService.HomeDataContext context = new IntranetService.HomeDataContext(intranetUri);

   4:  

   5: context.Credentials = System.Net.CredentialCache.DefaultCredentials;

   6:  

   7: IQueryable<IntranetService.TasksItem> tasks = from t in context.Tasks

   8:                                         where t.Title.Contains("Tarea")

   9:                                         select t;

  10:  

  11: foreach (var item in tasks)

  12: {

  13:     Console.WriteLine("ID {0} - Title {1}", item.ID, item.Title);

  14: }

  15:  

  16: Console.ReadLine();

Igual de simple lo tenemos para realizar inserciones o actualizaciones en la lista.

   1: IntranetService.TasksItem task = new IntranetService.TasksItem();

   2: task.Title = "Tarea 2 desde consola";

   3: task.StartDate = DateTime.Now;

   4: task.Created = DateTime.MinValue;

   5: task.Modified = DateTime.MinValue;

   6:  

   7: context.AddToTasks(task);

   8:  

   9: context.SaveChanges();

 

Aunque sigamos teniendo los clásicos Servicios Web (/_vti_bin/Lists.asmx) para consultar listas, este nuevo servicio con WCF Data Services nos proporciona un modelo de objetos relacional y un contexto que se encarga de realizar las consultas sobre el servicio.

 

Saludos a todos…

POCO Template para Visual Studio Beta 2

El equipo de ADO.NET acaba de publicar una nueva actualización de las plantillas que generan las clases POCO de nuestras entidades.

Las podemos descargar desde el Extension Manager de Visual Studio 2010 o desde las páginas de Visual Studio Gallery (c# o vb).

Estas plantillas nos permiten generar el código de las clases POCO y del ObjectContextl utilizando las plantillas T4.´

Si queremos más información sobre esta plantilla, la podemos leer en el blog de ADO.NET Team.

 

Saludos a todos…

Más rápido con Visual Studio 2010. Shortcuts

Vamos a recordar y aprender algunos de los Shotcuts que existen en Visual Studio 2010 que nos permiten ser más productivos, ahorrándonos levantar la mano del teclado y coger el ratón (que vagos que somos!).

Comment and Uncomment código (Ctrl-k-c y Ctrl-k-u)

Para comentar o descomentar las líneas de código que tengamos seleccionadas, tenemos la combinación de teclas Ctrl-k-c, para comentar, y Ctrl-k.u, para descomentar.

RapidoVS2010-Shortcuts1

También lo podemos utilizar con ficheros xml, aspx, etc.

RapidoVS2010-Shortcuts2

Toggle Outlining (Ctrl+m+m)

Para alternar la esquematización de un bloque de código, pulsaremos Ctrl+m+m, bien para cerrar o bien para abrir el esquema de código. Si lo que queremos es cerrar todos los esquemas de nuestro código, pulsamos las teclas Ctrl+m+l.

RapidoVS2010-Shortcuts3

Code Snippets (Ctrl+k+s)

Si queremos seleccionar un snippets de la lista, pulsamos Ctrl+k+s.

RapidoVS2010-Shortcuts4

View Call Hierarchy (Ctrl+k+t)

Para ver la jerarquía de llamadas de un método, nos posicionamos sobre el y pulsamos Ctrl+k+t.

RapidoVS2010-Shortcuts5

Esta ventana nos permite encontrar los lugares donde se llama a un método determinado, que es similar a cómo funciona actualmente buscar todas las referencias. Sin embargo, a diferencia de buscar todas las referencias, la función de Call Hierarchy proporciona mejor comprensión y más detallada acerca de las llamadas.

Un método (o una propiedad o un constructor) se muestra como una raíz en el treeview. Podemos expandir el nodo para obtener una lista de «categorías de búsqueda». Actualmente se admiten cuatro categorías de búsqueda:

  • Calls To – «entrante» llama a este miembro
  • Calls From – «saliente» llamadas mencionadas en el cuerpo de este miembro
  • Overrides – disponible sólo para miembros abstractos o virtuales
  • Implements – sitios donde se implementa un miembro de la interfaz

Las ventajas de Call Hierarchy frente a buscar todas las referencias es que permite explorar y profundizar en múltiples niveles en el gráfico de llamadas (del llamador encontrar llamador etc..) Por último, el panel de detalles muestra información sobre los sitios de llamada concretas, si un método se llama varias veces en el cuerpo del método que realizó llamada.

 

Saludos a todos…

Más rápido con Visual Studio 2010. Navegación y Búsquedas

Una nueva funcionalidad en Visual Studio 2010 es la ventana de Navegación (Navigate To – Ctrl+,). Esta ventana nos permite realizar búsquedas incrementales en las clases, ficheros, variables y miembros de nuestra solución, para luego,  al seleccionar, abrir el elemento y navegar hasta él.

RapidoVS2010-NavigateTo1

Navigate To realiza la localización de los elementos en nuestro código utilizando capacidades de búsqueda “fuzzy”. Por ejemplo, si buscamos Calendario laboral o laboral calendario es capaz de recuperar los elemento que coincidan con esa búsqueda, incluso cuando el elemento se llame CalendarioLaboral o incluso exista una clase Laboral en el namespace Calendario.

RapidoVS2010-NavigateTo2

También es capaz de realizar búsquedas con el formato Pascal Casing. Sólo tenemos que escribir las letras mayúsculas de nuestros tipos o miembros y automáticamente filtrará los resultados a las coincidencias de esta nomenclatura.

RapidoVS2010-NavigateTo3

Cuando seleccionamos un resultado de la búsqueda, nos mostrará el elemento marcándolo diferenciándolo del resto de nuestro código.

RapidoVS2010-NavigateTo4

Otra funcionalidad que nos aporta rapidez en el día a día, es la posibilidad de Navigate Backward (Navegar hacia atrás con Ctrl+-) o Navigate Forward (hacia adelante con Ctrl+Shift+-) en las posiciones en las que nos encontramos en nuestro código. Por ejemplo, si realizamos un Go To Definition (Ir a la definición de un tipo o clase con F12) y queremos volver a nuestro punto de partida, sólo tendremos que realizar la acción Navigate Backward. También podemos utilizar los botones de la barra de herramienta.

RapidoVS2010-NavigateTo5

También podemos realizar búsquedas de la palabra seleccionada o de la palabra en la que se encuentre el cursor sin necesidad de abrir el diálogo de búsqueda. Para ello sólo tenemos que posicionarnos en la palabra y pulsar Ctrl-F3, para búsquedas hacia adelante, y Ctrl+Shift+F3, para búsquedas en orden inverso o hacia detrás.

RapidoVS2010-NavigateTo6

Por ejemplo, si nos posicionamos en el primer String del código anterior, y realizamos una búsqueda hacia adelante con Ctrl+F3, se nos resaltará la primera coincidencia de la palabra String que encuentre en nuestro código.

RapidoVS2010-NavigateTo7

Espero que este pequeño resumen sobre navegación en Visual Studio nos permite ser más rápidos para localizar y navegar por nuestras soluciones.

 

Saludos a todos…