Los programadores SharePoint estamos tan acostumbrados a programar con ASP.NET Web Forms que no nos solemos preocupar por aprender otras tecnologías de programación ya que no se pueden aplicar directamente a SharePoint. Sin embargo, desde la versión 2010 de SharePoint, con el modelo de objetos de cliente (CSOM) podemos acceder a SharePoint desde cualquier aplicación .NET, incluyendo también el framework ASP.NET MVC, que vamos a introducir en este artículo.
Lo básico de MVC
En el mundo de ASP.NET Web Forms la base de todo es la página (instancia de clase Page). La página está mapeada físicamente a un fichero con extensión ASPX y dispara una serie de eventos (Init, Load, PreRender, Render etc) según vamos progresando de la petición HTTP a la respuesta HTML al cliente. Podemos decir que en cierto modo Web Forms implementa el patrón "Page Controller"[i].
En el mundo MVC esto ya no es así. La responsabilidad de servir la respuesta a una petición está dividida entre tres componentes que dan nombre al patrón de diseño MVC: modelo, vista y controlador.[ii]
El modelo son nuestras clases de negocio junto con su lógica de negocio. La vista es la renderización HTML de la respuesta al cliente mientras que el controlador es la lógica de la aplicación web que procesa el modelo y actualiza la vista. En el mundo Web Forms, la página hacía de vista y de controlador a la vez. Ahora estas dos cosas están bien separadas.
Las peticiones, rutas, acciones y otras cosas de nombre extraño
Vamos a ver como procesa ASP.NET MVC una petición y lo vamos a comparar con Web Forms para ver las diferencias. Supongamos que queremos ver un producto cuyo ID es 5. En Web Forms haríamos una página DisplayProduct.aspx y recogeríamos un parámetro desde Request.QueryString con el nombre ID. Es decir, la URL de la petición sería DisplayProduct.aspx?ID=5.
En MVC, la URL de la petición no apuntará a ningún fichero físico sino que nos indicara el controlador (de todos los controladores que hay), la acción (dentro de ese controlador) y posiblemente algún parámetro de esa acción. Una URL probable sería /Products/Display/5, indicando el controlador Products, acción Display con el parámetro 5.
Seguro que ahora os estáis preguntando como sabe MVC a que controlador tiene que enviar la petición y que vista se tiene que mostrar. Resulta que MVC por defecto interpreta la URL en el formato "controlador/acción/parámetro" y de esta manera la URL /Products/Display/5 equivale al invocar ProductsController.Display(5). ¿Y la vista? MVC la busca dentro de la subcarpeta Products de las vistas y buscará la que tiene como nombre Display. Como podéis ver, se favorece el uso de convenciones comunes de nombres para ahorrar código innecesario de infraestructura.
Una acción en el controlador será un método que devuelve un resultado, que casi siempre será una vista. La acción puede recibir parámetros (sencillos o incluso complejos) y puede pasarle los parámetros a la vista. Lo más habitual es que la acción realice alguna comprobación de validez de los parámetros y que use otras clases y servicios para llamar a la lógica puramente de negocio. Se suele utilizar inyección de dependencias para desacoplar aún más los controladores (lógica de la capa web de presentación) de la funcionalidad de negocio (capa de dominio o de negocio).
Las vistas en ASP.NET MVC se pueden hacer en Web Forms (ficheros ASPX) o con la nueva tecnología llamada Razor. Se recomienda Razor por ser mucho más sencillo y rápido. Razor tiene una sintaxis más minimalista y cuesta un cierto tiempo acostumbrarse a él. En contraprestación, se corresponde más al HTML final.
1@model MvcHelloWorld.Models.Product23@{45ViewBag.Title = "Display";67}89<h2>Display</h2>1011<p>@Model.Name <em>(@Model.ProductId)</em></p>12
Fijaos que Razor usa el prefijo @ para introducir su marcado de servidor. En este ejemplo le estamos diciendo a la vista que va a usar un tipo de datos como modelo (la clase MvcHelloWorld.Models.Product) y luego en el contenido de la página se usan las propiedades de ese modelo mediante la sintaxis @Model.
Hello, world en MVC
Para acabar de sentar el concepto de una aplicación MVC, vamos a hacer un ejemplo sencillo: una aplicación que permite ver la lista de productos y el detalle del producto. Para simplificar esta primera toma de contacto con MVC, vamos a usar la lista de productos fija implementada en la clase DBHelper. Más adelante usaremos la API de SharePoint para obtener la lista desde un sitio de SharePoint.
1public class DBHelper23{45public static Product GetProduct(int productId)67{89var product = GetProducts().Where(x => x.ProductId ==<br />productId).FirstOrDefault();1011return product;1213}14151617public static IEnumerable<Product><br />GetProducts()1819{2021List<Product> result = new List<Product>();22232425result.Add(new Product() { Name = "ACME Gadget", ProductId = 1 });2627result.Add(new Product() { Name = "XYZ<br />Widget",<br />ProductId = 2 });2829result.Add( new Product() { Name = "Product<br />ABC", ProductId = 4<br />});3031result.Add( new Product() { Name = "Foo<br />DEF", ProductId = 5<br />});32333435return result;3637}3839}40
Abrimos Visual Studio y creamos una aplicación web MVC 3 con la plantilla "Empty Application". Dejamos las opciones por defecto y nos encontramos con la siguiente estructura de un proyecto MVC.
Las carpetas más importantes son:
Ahora vamos a agregar el controlador. Clicamos con el botón derecho en la carpeta Controllers y elegimos Add / Add Controller. En el cuadro de diálogo, escribimos en nombre del controlador (ProductsController) y elegimos la plantilla "Empty Controller". Obtenemos el código siguiente:
1public class ProductsController : Controller23{45public ActionResult Index()67{89return View();1011}1213}14
La acción por defecto que se va a ejecutar si no
especificamos ninguna es la acción Index(). En esta acción vamos a mostrar la
lista de los productos, con el enlace a ver el detalle de cada uno de ellos.
Para ello hay que cambiar el código ligeramente y también vamos a aprovechar
para crear una acción nueva llamada Display:
1public ActionResult Index()23{45var products = DBHelper.GetProducts();67return View(products);89}10111213public ActionResult Display(int id)1415{1617var product = DBHelper.GetProduct(id);1819return View(product);2021}22
Todas las acciones devuelven un ActionResult que indica el resultado de una acción. En la cláusula return devolvemos el resultado de una vista, pasándole un objeto (la lista de productos o un solo producto). Pero, ¿cómo sabe MVC que vista tiene que devolver? Otra vez, lo hace siguiendo la nomenclatura de los nombres de métodos y controladores. Ahora no hay ninguna vista asociada pero lo vamos a corregir añadiendo una vista. Para ello, clicamos por el botón derecho sobre el nombre de la acción y elegimos "Add View". En el diálogo que nos sale elegimos la opción que vincula a una clase del modelo ("Generate strong-typed view").
Vamos a modificar la vista para que genere una lista de productos:
1@model IEnumerable<MvcHelloWorld.Models.Product>2345@{67ViewBag.Title = "Index";89}10111213<h2>Index</h2>14151617<ul>1819@foreach (var p in Model) {2021<li>2223@Html.ActionLink(p.Name,24"Display", new { id=p.ProductId })2526</li>2728}2930</ul>31
Como podéis ver, ahora la vista está ligada a un IEnumerable de productos (que es lo que le pasamos desde el controller). Además, creamos una lista de elementos que se generan usando una sintaxis de MVC llamada @Html. El método ActionLink nos genera en enlace a una acción, pasándole el nombre a mostrar, el nombre de la acción y un objeto con los parámetros de la acción (en nuestro caso sólo el ID del producto).
Nos falta crear la vista de la acción Display(), que generamos igual que la de Index() y le ponemos el código siguiente:
1@model MvcHelloWorld.Models.Product2345@{67ViewBag.Title = "Display";89}10111213<h2>@Model.Name</h2>14151617<p>1819<em>Product Id: @Model.ProductId</em>2021</p>22
Antes
de ejecutar el ejemplo, tenemos que decirle a MVC cuál es el controlador por
defecto y para ello editamos el método RegisterRoutes() en el fichero
Global.asax para apuntar a nuestro ProductsController en vez al controlador
Home que tiene la plantilla de MVC.
1routes.MapRoute(23"Default", // Route name45"{controller}/{action}/{id}", // URL with parameters67new { controller = "Products", action = "Index", id = UrlParameter.Optional<br />}89);10
¡Ya
estamos listos! Nuestra primera aplicación MVC está sólo a un F5 de distancia:
Hello, world en MVC y SharePoint
En este momento tenemos la aplicación web en MVC pero no usa SharePoint para nada. Para convertirla en aplicación de SharePoint vamos a agregar las referencias a la librería cliente de SharePoint para .NET (Microsoft.SharePoint.Client.dll y Microsoft.SharePoint.Client.Runtime.dll).
Tenemos un sitio de SharePoint que tiene la lista de productos. En mi ejemplo la lista está en un Office 365 pero podría estar perfectamente en cualquier SharePoint 2010 o 2013.
Ahora vamos a cambiar el método GetProducts() del DBHelper para que use SharePoint. Usaremos el modelo de objetos cliente de .NET para SharePoint. Para autenticarnos contra el servidor de SharePoint, tenemos que especificar la propiedad Credentials del ClientContext. Si usamos Office 365 con SharePoint 2013 tenemos unas credenciales específicas encapsuladas en la clase SharePointOnlineCredentials. Si es un SharePoint on-premise, usamos la clase NetworkCredential como siempre. Si tenemos la mala suerte de usar un SharePoint 2010 Online, podemos aprovechar la aportación de Chris Johnson para autenticarnos[iii].
1public static IEnumerable<Product><br />GetProducts()23{45ClientContext clientContext = new ClientContext("http://misharepoint.com");67SecureString password = new SecureString();89foreach (char c in "miPassword".ToCharArray())1011{1213password.AppendChar(c);1415}1617clientContext.Credentials = new NetworkCredential("miUsername", password, "miDominio");18192021List list = clientContext.Web.Lists.GetByTitle("Products");2223clientContext.Load(list);2425CamlQuery camlQuery = new CamlQuery();2627camlQuery.ViewXml = "<View/>";2829ListItemCollection listItems = list.GetItems(camlQuery);3031clientContext.Load(listItems, items =><br />items.Include( item=>item["Title"],<br />item => item.Id));3233clientContext.ExecuteQuery();3435List<Product><br />result = new List<Product>();3637foreach (ListItem listItem in listItems)3839{4041var product = new Product() { ProductId = listItem.Id, Name<br />= listItem["Title"].ToString() };4243result.Add(product);4445}4647return result;48}49
Como podéis ver, la consulta a SharePoint es directa: obtenemos los datos de la lista Products y mapeamos los objetos ListItem a instancias de la clase Product, usando las propiedades Id y Title de SharePoint como las correspondientes a ProductId y Name de nuestro modelo.
Podemos ver que con este pequeño ejemplo ya tenemos nuestra aplicación MVC conectada a SharePoint, pulsando F5 para ejecutarla.
El código fuente de esta aplicación de ejemplo está disponible en la dirección http://sdrv.ms/XRmW0O.
¿Y ahora qué?
Espero que esta pequeña introducción sirva para que los desarrolladores de SharePoint exploren el mundo de ASP.NET MVC y pierdan el miedo a una plataforma a la que no están tan acostumbrados. En las próximas entregas veremos más detalles sobre las aplicaciones MVC aplicadas a SharePoint como por ejemplo como guardar datos, hacer validaciones y usar inyección de dependencia para desacoplar nuestra aplicación y poder realizar pruebas unitarias. ¡Nos vemos en el próximo número!
Edin Kapić Arquitecto SharePoint ekapic@pasiona.com @ekapic http://www.pasiona.com
[i] http://msdn.microsoft.com/en-us/library/ff649595.aspx
[ii] http://msdn.microsoft.com/en-us/library/ff649643.aspx [iii] http://blogs.msdn.com/b/cjohnson/archive/2011/05/03/authentication-with-sharepoint-online-and-the-client-side-object-model.aspx