Asp net mvc Una gua introductoria

July 2, 2016 | Author: willvyxp | Category: Types, Presentations
Share Embed Donate


Short Description

Asp net mvc Una gua introductoria...

Description

ASP.NET MVC 6 - UNA GUÍA INTRODUCTORIA

Walter Montes Delgado

ASP.NET MVC 6 - UNA GUÍA INTRODUCTORIA Walter Montes Delgado This book is for sale at http://leanpub.com/aspnetmvc6-unaguaintroductoria This version was published on 2015-05-15

This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and many iterations to get reader feedback, pivot until you have the right book and build traction once you do. ©2015 Walter Montes Delgado

Contents ACERCA DEL AUTOR . . . . . . . . . . . . . . . . . . . .

1

INTRODUCCIÓN PERSONAL - PORQUÉ UNA GUÍA INTRODUCTORIA A MVC . . . . . . . . . . . . . . . . .

2

CAPÍTULO UNO – UNA INTRODUCCIÓN A .NET 2015 Y ASP.NET 5 . . . . . . . . . . . . . . . . . . . . . . . .

4

CAPÍTULO DOS – EL PATRÓN QUE TODOS AMAN, MVC

7

CAPÍTULO TRES – ASP.NET MVC 6 SIN PERDERSE Una pequeña introducción al desarrollo web . . . . ASP.NET MVC 6 . . . . . . . . . . . . . . . . . . . VISTAS . . . . . . . . . . . . . . . . . . . . . . . . CONTROLADORES . . . . . . . . . . . . . . . . . MODELOS . . . . . . . . . . . . . . . . . . . . . . EJERCICIOS . . . . . . . . . . . . . . . . . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

9 9 10 22 26 29 29

CAPÍTULO CUATRO – CARACTERÍSTICAS AVANZADAS Pase de información entre el controlador y la vista . . . . Validaciones . . . . . . . . . . . . . . . . . . . . . . . . . Vistas parciales vs ViewComponents (VCs) . . . . . . . . Agregando una base de datos . . . . . . . . . . . . . . .

34 34 38 44 50

CONCLUSIÓN . . . . . . . . . . . . . . . . . . . . . . . . .

56

ACERCA DEL AUTOR Walter Montes tiene múltiples años trabajando con tecnología Microsoft desde desarrollo de software, instalación y configuración de productos hasta arquitectura de soluciones. Al momento de escribir este libro, es el único Microsoft Most Valuable Professional (MVP) en ASP.NET/IIS de Centroamérica. Además de ser el administrador de la comunidad oficial de desarrolladores .NET en Costa Rica CR Developers .NET. Cuenta con su propio blog en tecnología Microsoft y Open Source en la siguiente dirección: http://waltermontes.com¹ ¹http://waltermontes.com

INTRODUCCIÓN PERSONAL - PORQUÉ UNA GUÍA INTRODUCTORIA A MVC Para nadie es un secreto que .NET Framework de Microsoft es una plataforma lo suficientemente robusta como para soportar millones de aplicaciones críticas en las organizaciones, que además estará con nosotros por muchísimos años en el futuro. Con el crecimiento de la internet, Microsoft ha escuchado a los desarrolladores, empresas y emprendedores, a raíz de esto ha orientado el desarrollo de su Framework hacia algo que hoy en día con .NET 2015 es una plataforma de desarrollo abierta, inclusiva y multiplataforma. Sumado a todo esto que se ha ido viendo en los últimos años con la plataforma de Microsoft, está la orientación de ASP.NET hacia el software abierto y a cumplir y trabajar agradablemente con estándares mundiales que otras plataformas implementan. Prueba de esto fue el nacimiento de ASP.NET MVC, el cual le permite a los desarrolladores poder separar mejor las responsabilidades de un sistema, conocer completamente el markup de su aplicación y a entender como es la comunicación entre el cliente y el servidor, permitiendo optimizar sus sistemas. Es por esto que he tomado la decisión de aportar a la comunidad hispana con un libro sencillo y al grano de ASP.NET MVC 6, para ayudarles a esas personas que apenas están entrando al mundo de MVC a entender este patrón y que las personas que ya lo conocen que puedan validar fundamentos básicos mientras logran adentrarse un poco más. Previo a iniciar a leer este libro sería ideal que ya conozca las siguientes tecnologías al menos a un nivel básico: • C#

INTRODUCCIÓN PERSONAL - PORQUÉ UNA GUÍA INTRODUCTORIA A MVC

3

• HTML • CSS • JavaScript Todo lo presentado en este libro representa mi punto de vista personal y no busca representar a ningún empleador ni a Microsoft como corporación.

CAPÍTULO UNO – UNA INTRODUCCIÓN A .NET 2015 Y ASP.NET 5 Antes de entrar en materia, es importante entender los cambios que .NET Framework está teniendo para conocer cómo impacta esto a ASP.NET. Probablemente mucha de la información que será presentada a continuación pueda ser confusa si estás empezando en .NET o ASP.NET, así que el consejo sería que más adelante vuelvas a esta sección y le des otra leída posterior a finalizar el libro. .NET 2015 es el nombre que se le da a la nueva versión de .NET Framework y un buen punto de arranque es ver un diagrama como Microsoft presenta esta nueva versión liberada.

Vista de alto nivel de .NET 2015

Durante este libro se tomará un enfoque principalmente en a donde está ubicado ASP.NET dentro de esta “sombrilla” pero igualmente les insto a conocer todo el mapa de .NET Framework y en mi

CAPÍTULO UNO – UNA INTRODUCCIÓN A .NET 2015 Y ASP.NET 5

5

parecer personal, principalmente sobre C# 6, Roslyn, CoreCLR y .NET Native. Lo más notorio de este diagrama es la separación de .NET Framework en dos bloques: .NET Framework 4.6 y .NET Core 5. .NET Framework 4.6*: Continúa el trabajo que trae .NET 4.5.2 con muchísimas mejoras alrededor del framework. Se puede ubicar como el framework que viene incluido en el sistema operativo, en este caso en Windows 10 y el cual recibe actualizaciones a través del conocido Windows Update. Es importante notar que sobre .NET Framework 4.6 están las tecnologías WPF, Windows Forms y ASP.NET versiones 4 y 5. .NET Core 5: Microsoft lo describe como un “framework modular”, el cual llega a nosotros como una versión de software abierto, el cual puede ser desplegado de manera modular y local, además de ser mucho más ligero. Al ser modular busca también ser multiplataforma, corriendo en Windows, Linux y OSX. A diferencia de .NET Framework 4.6, .NET Core 5 permite correr aplicaciones ASP.NET solamente en la versión 5 y Universal Windows Apps con .NET Native. De esta forma ASP.NET se ubica de la siguiente manera dentro del universo .NET.

Donde está ASP.NET 5 en el universo .NET

Con ASP.NET 5 corriendo en ambas “ediciones” del framework se logra desplegar y desarrollar aplicaciones web tanto en Windows (sobre .NET Framework 4.6 o corriendo lado a lado sobre .NET Core

CAPÍTULO UNO – UNA INTRODUCCIÓN A .NET 2015 Y ASP.NET 5

6

5 con otra versión de .NET Framework instalada en el servidor) como en Linux y OSX. A este punto .NET Core 5 soporta solamente ASP.NET MVC en C#, es decir, no Web Forms ni VB.NET. Esto no significa que no pueda ser que Microsoft incluya soporte a Web Forms o VB.NET en el futuro. Por otro lado .NET Framework 4.6 si continúa soportando el modelo de desarrollo en Web Forms y el lenguaje de programación VB.NET. Algunas de las características más notables de ASP.NET 5 es la unificación de MVC, Web API y Web Pages en un solo modelo conocido como MVC 6. Otra importante adición es la integración con herramientas populares de desarrollo web como Bower, Grunt y Gulp, los cuales ya se podían utilizar con otros frameworks de desarrollo como PHP, Node.JS y Ruby.

CAPÍTULO DOS – EL PATRÓN QUE TODOS AMAN, MVC Modelo Vista Controlador, es lo que las siglas MVC representan, y su objetivo principal es lograr la separación entre estos tres “personajes”, lo cual permite, a través de este patrón de diseño, crear aplicaciones robustas tomando en consideración buenas prácticas aprendidas de otras plataformas de desarrollo y del propio Microsoft.

Modelo Vista Controlador

La separación de conceptos busca precisamente que cada “bloque” de la aplicación realice solo el trabajo que le corresponde, así logra obtener otras ventajas tales como mantenibilidad del sistema, extensibilidad y crecimiento ordenado. Así como otros patrones ya buscan logra esto, tales como progra-

CAPÍTULO DOS – EL PATRÓN QUE TODOS AMAN, MVC

8

mación en N-Capas, MVC puede seguir estos lineamientos como separación de proyectos para distribuir componentes comunes. Todo esto es por MVC como tal es un patrón, no una tecnología. Vista La vista es el conjunto de markup de la aplicación, es decir el HTML. En ASP.NET MVC este markup es pre renderizado a través de una tecnología llamada Razor, la cual permite, entre otras cosas, ejecutar lógica previa a renderizar el HTML que será enviado al cliente. Con Razor esta compilación o renderización ocurre del lado servidor. No debería incluir ningún tipo de lógica de negocio, sino más bien lógica propia de interacción con el usuario, como iterar una lista (List) y presentar una lista ordenada (ul) de ítems al usuario. Modelo Son los objetos de dominio o negocio de la aplicación, más adelante, veremos que no es necesario que esté en el mismo proyecto que la aplicación web. Controlador El controlador se encarga de recibir solicitudes y ser el punto de entrada de la aplicación, a través de lo que se conoce como acciones (Actions). Se basa en solicitudes HTTP y puede responder con datos o con contenido, como HTML.

CAPÍTULO TRES – ASP.NET MVC 6 SIN PERDERSE Una pequeña introducción al desarrollo web Esta sección es una introducción básica y esencial de desarrollo web, si ya estás familiarizado con desarrollo web en otras tecnologías, probablemente no sea necesario que pases por esta sección. En el desarrollo web, se trabaja con un servidor que responde archivos e información y un navegador que ejecuta y lee los archivos. Por ejemplo:

Ambiente web

El cliente inicia una solicitud por HTTP al servidor, por ejemplo http://www.bing.com², y el servidor, a través de ASP.NET procesa la solicitud, contacta bases de datos y demás, y retorna páginas HTML renderizadas, archivos CSS, JavaScript y medios (imágenes, videos, etc). ²http://www.bing.com

CAPÍTULO TRES – ASP.NET MVC 6 SIN PERDERSE

10

HTML: markup de la página JavaScript: lógica de lado cliente (pc de usuario) CSS: hojas de estilo La comunicación entre el cliente y el servidor se realiza a través de HTTP por lo que cada solicitud se conforma de una solicitud (request) y una respuesta (response). Las solicitudes web tienen como mínimo un encabezado (header) y un cuerpo (body). El encabezado indica dirección de la solicitud, tipo de solicitud e información del contenido aceptado, entre otros; y el cuerpo incluye información que se envía al servidor. De esta forma el tipo de solicitud se define a través de un verbo HTTP que indica el tipo de solicitud, los principales verbos que se deben conocer son: * GET: usualmente conocido como verbo “obtener”, no incluye cuerpo • POST: solicitud de envío de información, debe ir acompañado de un cuerpo de contenido, por ejemplo una imagen o un JSON • PUT: similar al POST, sin embargo se utiliza para cuando se realizan actualización de recursos • DELETE: similar al en estructura al GET, pero el servidor debe entender que se busca remover el recurso que se consulta en lugar de retornarlo

ASP.NET MVC 6 ¿Qué mejor forma de comenzar a aprender una tecnología que ir directo al código y explorarlo? Pues ese será el siguiente pasó en este libro. Durante todo el recorrido se irán mencionando algunos conceptos importantes y al final un par de ejemplos.

CAPÍTULO TRES – ASP.NET MVC 6 SIN PERDERSE

11

En el momento de escribir este libro la versión de Visual Studio 2015 que se puede utilizar para desarrollar MVC 6 está en RC (Release Candidate). Antes de comenzar, es importante entender que una plantilla de Visual Studio, no es más que una serie de DLLs que funcionan para un fin específico incluidas en un proyecto + una base de código configurativa de la tecnología + uno o varios ejemplos de código. Es decir, si uno quisiera hasta podría empezar un proyecto sin necesidad de seleccionar un template de Visual Studio. La forma de “unificar” archivos en forma de proyecto es a través de archivos con extensión .csproj o .vbproj, los cuales a su vez se pueden agrupar en una solución con archivos extensión .sln. Para entender un poco más de archivos de proyecto y su funcionalidad.

CAPÍTULO TRES – ASP.NET MVC 6 SIN PERDERSE

12

Estructura de solución

Archivos no específicos de usuario: archivos propios del software, que pueden ser controlados por un manejador de versiones tal como TFS o Github. • -sln • .csproj o .vbproj Archivos específicos de usuario: archivos propios el software desarrollado para el usuario de sistema actual. No son controlados por controladores de versiones. • -suo

CAPÍTULO TRES – ASP.NET MVC 6 SIN PERDERSE

13

• .csproj.user o .vbproj.user Volviendo al tema de ASP.NET MVC el proceso de creación de un proyecto basado en una plantilla es sencillo, en Visual Studio > File/Archivo > New/Nuevo> Project/Proyecto

Nuevo Proyecto

El siguiente paso es seleccionar la plantilla de MVC 6, debe fijarse que seleccione .NET Framework 4.6 y colocar un nombre de proyecto y de solución acorde a sus necesidades ya que este será el nombre de espacio que seguirá su proyecto (orden interno del proyecto). Usualmente y por estándar de Microsoft³ se nombra el nombre de espacio de la siguiente forma ³https://msdn.microsoft.com/en-us/library/ms229002(v=vs.110).aspx

CAPÍTULO TRES – ASP.NET MVC 6 SIN PERDERSE

14

Definición de la plantila MVC 6

Para los que vienen desde versiones anteriores previo a Visual Studio 2012, ha visto un par de cosas nuevas, entre ellas, que no tuvieron que seleccionar el tipo de proyecto (Web Forms, MVC o Web Pages), sino que se selecciona un proyecto de tipo ASP.NET, esto es por un cambio que se dio en el 2013, en el cual aparece el término ONE ASP.NET, el cual busca que se puedan crear proyectos con tecnología Web Forms y MVC/Web API en un solo proyecto de Visual Studio, a diferencia de como anteriormente solo se podía seleccionar un tipo. La siguiente pantalla que aparece a continuación da la opción de seleccionar una nueva plantilla de proyecto ASP.NET 5. En este caso se debe seleccionar Web Site, el cual crea una plantilla para un sitio web MVC y Web API.

CAPÍTULO TRES – ASP.NET MVC 6 SIN PERDERSE

15

Tipo de Proyecto ASP.NET

En la imagen anterior se ha resaltado la opción “Change Authentication” para dar un poco de énfasis a esta opción. Si se le da clic a este botón, aparece la siguiente pantalla.

Cambiar autenticación

Esta ventana da la opción de seleccionar la estrategia de autenticación, por lo cual se debe tener de antemano una idea de la estrategia de autenticación y autorización del sistema. Esto porque

CAPÍTULO TRES – ASP.NET MVC 6 SIN PERDERSE

16

más adelante se puede cambiar, sin embargo puede ser un poco complicado si no se conoce bien a detalle cómo se implementa cada modelo, ya que al seleccionar una opción se crean las clases y configuraciones de ASP.NET y/o IIS para procesar un modelo de autenticación. ¿Cómo determinar el modelo de autenticación? • No Authentication: en caso de que no se requiera autenticación o se desee implementar un tipo especial de autenticación • Individual User Accounts: en caso de que se quiera contar con usuarios propios para el sistema, es decir, con una base de datos SQL Server que almacene usuarios y contraseñas de manera segura y con buenas prácticas, y/o cuando se quiere autenticar usuarios con una tercera parte, tal como Facebook, Twitter, Google o Microsoft. • Work And School Accounts: permite autenticar con proveedores de autenticación empresiales, tal como Azure Active Directory o Active Directory Federation Services. • Windows Authentication: en ambientes corporativos cuando las computadoras clientes están en un dominio en Active Directory Domain Services, de esta forma no se solicita al usuario autenticarse, sino que se utilizan los credenciales de Windows. Para la demostración se va a dejar marcada la opción de Individual User Accounts, el cual utiliza el Simple Membership Provider para obtener autenticación de usuarios en una base de datos SQL Server en LocalDB (más adelante se ahondará un poco más en este tema). Al terminarse de aprovisionar el proyecto es probable que el nodo “References” aparezca de esta forma References (Restoring) por unos segundos mientras se terminan de descargar y aprovisionar las DLLs necesarias. Al finalizar se deberá ver una estructura de folders y archivos similar a la de la imagen de la derecha.

CAPÍTULO TRES – ASP.NET MVC 6 SIN PERDERSE

17

En esta nueva versión de .NET y Visual Studio, NuGet toma mucho protagonismo, ya que se busca que con .NET Core solo se traiga lo mínimo necesario y lo demás que se ocupe sea descargado a través de NuGet. Esta nueva estructura es muy similar a las de los proyectos en Github o Codeplex.

Estructura Proyecto

Los primeros niveles son: * Solutions Items * src

CAPÍTULO TRES – ASP.NET MVC 6 SIN PERDERSE

18

El primer archivo global.json dentro del folder Solution Items es el archivo que contiene la información del proyecto para Visual Studio u otras herramientas, en este caso el archivo está casi vacío, solo contiene información de las carpetas y versión del SDK. Para los que vienen de versiones anteriores de .NET ver archivos configurativos con extensión JSON es algo totalmente nuevo, por otro lado para los que vienen de otras plataformas como Node.JS es algo muy común y natural.

Archivo global.json

En el siguiente folder src donde se ven los archivos propios del proyecto, como ya se han conocido anteriormente. Dentro del folder src parecen los proyectos de la solución, en este ejemplo solo existe el proyecto de Interfaz de Usuario “wm.website.ui”. En este nuevo proyecto de MVC 6 aparece un folder llamado wwwroot, (nuevo en esta versión) el cual contiene archivos que no deben ser compilados, tales como hojas de estilo (CSS), imágenes y archivos de JavaScript.

CAPÍTULO TRES – ASP.NET MVC 6 SIN PERDERSE

19

Folder wwwroot

A nivel de raíz de proyecto hay otros archivos extensión .json, tales como bower.json, config.json, package.json y project.json. ¡Sí! No hay web.config por defecto, sin embargo ASP.NET 5 soporta archivos configurativos en formato XML, INI o JSON. Para tratar de entender todo este cambio configurativo se hará un pequeño overview de cada archivo. bower.json El archivo bower.json, contiene, como su nombre lo dice, configuraciones propias de bower.

CAPÍTULO TRES – ASP.NET MVC 6 SIN PERDERSE

20

Bower

¿Qué es bower? Bower, al igual que NPM son administradores de paquetes, algo así como NuGet pero se comparten entre diferentes tecnologías ya que usualmente no dependen de la tecnología backend. En este caso Bower permite manejar dependencias de lado cliente, tales como jquery, bootstrap, etc. config.json, package.json y project.json Entre estos proyectos prácticamente se distribuyen las configuraciones que anteriormente existían en el archivo web.config. Si eres nuevo en desarrollo web sobre .NET, el web.config era un archivo XML que contenía configuraciones propias del proyecto. Durante este libro no se va a ahondar tantísimo en cada detalle nuevo, más bien, se intentará que sea algo más funcional y directo a entender lo mínimo necesario de ASP.NET 5.

CAPÍTULO TRES – ASP.NET MVC 6 SIN PERDERSE

21

El archivo Startup.cs es el que invoca los .json e inyecta dependencias necesarias (más adelante se conversará un poco más al respecto). Entrando ya en temática de propia ya de MVC, se pueden observar los folders Controllers, Views y Models. Para correr inicialmente la plantilla se debe presionar F5 y se podrá observar la nueva plantilla corriendo. Este template ya tiene incluidas las librerías jQuery⁴ y Bootstrap⁵, además de otras más. Por lo cual ya trae un diseño inicial y unas páginas iniciales que responden solicitudes a través de controladores.

Ejecución básica ⁴http://jquery.com ⁵http://getbootstrap.com/

CAPÍTULO TRES – ASP.NET MVC 6 SIN PERDERSE

22

VISTAS Lo primero que se verá en esta línea serán las vistas y cómo funcionan entre sí. ¿Dónde están ubicadas? En el folder Views ¿Qué extensiones tienen? .cshtml o .vbhtml ¿Qué lenguaje se utiliza? HTML a través del pre compilador de HTML llamado Razor Todo website tiene ciertos componentes que se repiten entre páginas, tipo “marco”. En Web Forms se le conocía como MasterPage, en MVC se le conoce como _Layout. Usualmente estas secciones incluyen partes como encabezados, pie de página y menús de navegación. Por ejemplo en la imagen de la derecha lo que está en celeste sería el layout.

Layout

_layout.cshtml Este archivo incluye todo el marco y plantilla HTML, es decir, tags como , Al abrir este archivo se puede apreciar algo nuevo en esta versión de ASP.NET, el tag “environment” el cual permite, según el despliegue que se realice, agregar unos u otros archivos de scripts y hojas

CAPÍTULO TRES – ASP.NET MVC 6 SIN PERDERSE

23

de estilos. Por ejemplo para el momento de desarrollo sirven los archivos fuentes iniciales, sin embargo en producción usualmente los archivos que se envían a un usuario son los archivos de hojas de estilo y scripts minificados y obfuscados. Esto para evitar el uso innecesario de ancho de banda y complicar la lectura del código fuente que se descarga del lado cliente y que durante desarrollo se pueda realizar depuración de código JavaScript.

Etiqueta Environment

Al investigar un poco más el archivo se puede encontrar la sentencia @RenderBody() en esta parte del HTML se renderizarán el resto de páginas que utilicen este archivo de layout. La manera de asignar este layout a diferentes páginas es a través de una variable llamada Layout. Es decir si en alguna vista se asigna algún valor a esa variable global, esa página utilizará ese archivo de layout. Con el siguiente diagrama se intentará explicar un poco más claro esto.

CAPÍTULO TRES – ASP.NET MVC 6 SIN PERDERSE

24

Renderización vista

Como se pudo notar al ejecutar el proyecto, la página que fue retornada ya traía un layout y contenido renderizado, esto es porque por defecto cada vista creada tiene un layout por defecto. Esta configuración se realiza en el archivo _ViewStart.cshtml. Es decir, si no se indica explícitamente en cada vista cual archivo de layout utilizar, la vista utilizará el que se indique en el archivo _ViewStart.cshtml

Asignación Layout

La sentencia anterior definida por @{ } Permite incrustar código C#, se conocen como bloques de código (code blocks) y permiten ejecutar lógica, por ejemplo:

CAPÍTULO TRES – ASP.NET MVC 6 SIN PERDERSE

25

Bloque de código

El símbolo @ también permite utilizar también lo que se conoce como Tag Helpers. Los cuales básicamente renderizan bloques de HTML basándose en cierta lógica, fuera de caja se incluyen los conocidos como HTML Helpers. Existen diferentes Tag helpers que se pueden utilizar por ejemplo optar por el que ha venido desde versiones anteriores:

HTML Helper

O utilizar uno de los nuevos tag helpers que permite un código más “limpio” al utilizar atributos de HTML. Por ejemplo:

Nuevo Tag Helpers

Al fin y al cabo ambos renderizan al final el siguiente bloque de código HTML: Click here to Log in.

Vistas de contenido

CAPÍTULO TRES – ASP.NET MVC 6 SIN PERDERSE

26

Las vistas contienen el HTML base + lo renderizado por el Razor. Su contenido dependerá de la información que se desea mostrar, más adelante se mostrará cómo se asocia con un controlador y con un modelo.

CONTROLADORES Un controlador se conforma de una clase con funciones que responden solicitudes HTTP. La clase equivale al nombre del controlador y la función equivale al nombre de la acción. Nombre de Clase = Controlador Nombre de Función = Acción Por ejemplo en el archivo Controllers/HomeController

Clase de controlador

De esa manera, se crea un extremo (endpoint web) que puede ser consultado a través de un cliente que pueda realizar solicitudes HTTP (un navegador, un robot, un dispositivo). Por ejemplo según la imagen anterior en HomeController existen las siguientes 2

CAPÍTULO TRES – ASP.NET MVC 6 SIN PERDERSE

27

rutas que responden solicitudes, acorde a la siguiente convención http://localhost: • http://localhost: • http://localhost: (*) nota: La palabra “controller” en la clase es removida del nombre. Así las solicitudes web se resuelven de la siguiente forma:

Procesamiento solicitudes web

Entra la solicitud iniciada desde el cliente (pc o dispositivo), el servidor busca que haya una ruta que responda a esa url y responde el acción que corresponde. Como pudieron notar al ejecutar este proyecto la url http://localhost: • Si no se indica la acción que se busca alcanzar, se utiliza por defecto la que se llame Index • Si no se indica el controlador que se busca alcanzar, se utiliza por defecto la que se llama Home Esto se define a través del archivo Startup.cs en el siguiente bloque de código:

CAPÍTULO TRES – ASP.NET MVC 6 SIN PERDERSE

28

Plantilla de ruteo

En esa sección de código se define la convención a seguir, es decir {controlador}/{acción}/{id opcional} El controlador en el tipo de función define qué tipo de dato se retornará al cliente. Por ejemplo en el homecontroller, todas las acciones responden un tipo de dato IActionResult, en este caso todas las funciones retornan un objeto de tipo View.

Plantilla de ruteo

La función View se puede colocar sin pasar parámetros por lo cual la función buscará una vista que esté en el folder *Views/ A pesar de todo esto, en lugar de un View se pueden retornar cualquier tipo de datos que herede de IActionResult. Comentarios de código Existen 3 tipos de comentarios en Razor: * Comentarios HTML, son

CAPÍTULO TRES – ASP.NET MVC 6 SIN PERDERSE

29

retornados al cliente en el HTML * * Comentarios internos, no son retornados al cliente * @* este es un comentario *@ * Comentarios dentro de bloques de código, no son retornados al cliente HTML * @{ // este es un comentario } Es importante saber qué tipo de comentario utilizar, para evitar estar retornando al cliente comentarios internos.

MODELOS Los modelos son clases que representan objetos de dominio y pueden ser utilizados por la lógica de negocio y acceso a datos para manipular información. Están ubicado en la plantilla en el folder Models.

EJERCICIOS Agregar una nueva vista Para este ejercicio se necesitará una nueva vista que sea retornada al hacer una consulta HTTP a un url. 1. En el archivo Controllers/HomeController.cs crear una función que permita retornar una nueva vista 1 2 3 4

public IActionResult VistaPersonalizada() { return View(); }

2. Posterior a crear el extremo que ya recibe las solicitudes web, se debe crear una vista que sea retornada por la función View.

CAPÍTULO TRES – ASP.NET MVC 6 SIN PERDERSE

30

3. Escribir algún contenido en la vista recién creada

4. Probar el código ejecutando el proyecto (F5) y navegando a la ruta: http://localhost: 5. Disfrutar el resultado

CAPÍTULO TRES – ASP.NET MVC 6 SIN PERDERSE

31

Comunicación cliente hacia servidor Para este ejercicio se necesitará un formulario apuntando a través de un método de tipo post a una acción en Home y este retornará una vista. 1. En el archivo Views/Home/Index.cshtml agregar un formulario apuntando a una acción del controlador Home, al comienzo del archivo 1 2 3 4 5 6

Submit

2. Crear la acción que responda a esta solicitud, en el archivo Controllers/HomeController.cs agregar una función como la siguiente

CAPÍTULO TRES – ASP.NET MVC 6 SIN PERDERSE

1 2 3 4 5 6 7

32

[HttpPost] public IActionResult SubmitForm(string nombre, int eda\ d) { var nombreRecibido = nombre; var edadRecibida = edad; return View("Index"); }

3. Crear un breakpoint en la línea de código de la función.

4. Ejecutar el código, ingresar datos y dar clic a submit.

5. Visual Studio detectará el Breakpoint y este permitirá ver si se pasaron correctamente los valores.

¿Qué se puede notar del ejercicio anterior? • Los atributos personalizados asp-controller y asp-action, los cuales renderizan en el atributo action=”Home/SubmitForm”

CAPÍTULO TRES – ASP.NET MVC 6 SIN PERDERSE

33

• El uso del atributo name en los inputs para ser relacionado con los parámetros de la función, el cual permite realizar la “liga” entre los valores enviados por el formulario con los recibidos en la función. De esta forma si hay un input con el atributo name y la acción a la que se apunta recibe un parámetro con el mismo nombre al valor del atributo name, ASP.NET los relaciona y asigna el valor a la variable. • El uso del atributo [HttpPost] en la función SubmitForm, el cual diferencia que esta acción responderá solo a solicitudes de tipo POST. – Si no se indica este atributo en la función, por defecto se creará la función respondiendo a solicitudes tipo GET.

CAPÍTULO CUATRO – CARACTERÍSTICAS AVANZADAS En el capítulo anterior se hizo un recorrido inicial por ciertas funcionalidades principales de ASP.NET MVC 6. En el cual se pudo conocer lo básico de cada componente y cómo lograr comunicación y empezar a extender el proyecto básico.

Pase de información entre el controlador y la vista Para compartir información entre una acción y una vista existen diversas estrategias que se pueden utilizar, tales como: * ViewData permite pasar datos entre un controlador y una vista, solo sobrevive durante el solicitud actual * ViewBag es una propiedad dinámica que permite utilizar “dynamics” de C# 4.0, solo sobrevive durante la solicitud actual. Usualmente sirve para pasar datos sencillos. * TempData permite pasar información entre solicitudes subsecuentes (por ejemplo redirección) * Modelo este modo permite asignar a una vista un modelo específico y pasarla por parámetros al retornar la vista Esta sección busca explicar un poco más como utilizar Razor y hacer algunas cosas un poco más complejas tales como renderizar listas y pasar información por ViewBag. Adentrándonos a Razor, renderizar una lista 1. Crear un controlador llamado ItemsController

CAPÍTULO CUATRO – CARACTERÍSTICAS AVANZADAS

35

2. Crear un folder en la ruta Views/Items y una vista llamada Index dentro de ese folder 3. Crear un modelo llamado Items con el siguiente contenido. Clic derecho en el folder Models > Add > New Item…

CAPÍTULO CUATRO – CARACTERÍSTICAS AVANZADAS

36

4. Y se debe agregar este contenido 1 2 3 4 5 6

[HttpPost] public class Item { public int Id { get; set; } public string Nombre { get; set; } }

5. En el controlador Items en la acción Index se deben crear algunos ítems de prueba para luego renderizarlos en la vista, de la siguiente forma 1 2 3 4

List items = new List(){ new Item() { Id=1, Nombre="Item1" }, new Item() { Id=2, Nombre="Item2" }, new Item() { Id=3, Nombre="Item3" }};

6. Para renderizarlos en la vista Index, se debe pasar a través de parámetros de la sentencia View(), en el mismo archivo ItemsController, posterior a inicializar la lista

CAPÍTULO CUATRO – CARACTERÍSTICAS AVANZADAS

1

37

return View(items);

7. A la vista se le debe indicar que utilizará este tipo de modelo, para esto se usa la sentencia @model al comienzo del archivo de vista. En el archivo Views/Items/Index borre todo el contenido y agregue la siguiente línea de código 1

@model List

8. Lo siguiente sería iterar en la lista que está siendo retornada y renderizar uno a uno los ítems. Use el siguiente bloque de código para este fin. Como podrá notar se utiliza el carácter @ para ejecutar lógica en el archivo Razor 1 2 3 4 5 6

@foreach(var item in Model) { id: @item.Id nombre: @item.Nombre }

9. Ahora ejecute la aplicación (F5) y vea el resultado en el navegador en la dirección http://localhost:

10. Pasar información por ViewBag 11. En el mismo controlador, agregue la siguiente línea de código dentro de la acción Index en cualquier parte antes de la sentencia View.

CAPÍTULO CUATRO – CARACTERÍSTICAS AVANZADAS

1

38

ViewBag.Mensaje = "¡Este valor es pasado por ViewBag!";

12. En la vista agregue un poco de markup para mostrar el valor en la vista de la siguiente forma 1 2 3 4 5 6 7 8 9 10

@model List @ViewBag.Mensaje @foreach(var item in Model) { id: @item.Id nombre: @item.Nombre }

13. Ahora ejecute la aplicación (F5) y vea el resultado en el navegador en la dirección http://localhost:

Validaciones En un sistema robusto, deben existir validaciones de datos de lado cliente y de lado servidor, si bien estas se pueden crear manualmente, “uno a uno”, esta tarea puede ser un poco engorrosa

CAPÍTULO CUATRO – CARACTERÍSTICAS AVANZADAS

39

y compleja de mantener. Para esto ASP.NET MVC cuenta con una librería llamada DataAnnotations la cual permite definir qué validaciones se necesitan en el formulario de lado servidor y a través de jQuery Validate validar lado cliente. Las dataannotations están en el nombre de espacio System.ComponentModel.DataAnnotations y permiten no solo especificar cierta información de validación, sino también ciertos metadatos que Razor entenderá, como cual texto mostrar en el label. DataAnnotations 1. Vaya al archivo Models/Item.cs y reemplace el contenido de la clase por el siguiente. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

using System.ComponentModel.DataAnnotations; namespace .Models { public class Item { [Required(ErrorMessage = "El identificador del\ ítem es obligatorio")] public int Id { get; set; } [Required(ErrorMessage ="El nombre del ítem es\ obligatorio")] [Display(Name ="Nombre de ítem")] [MaxLength(10, ErrorMessage ="El nombre del ít\ em no debe superar los 10 caracteres")] public string Nombre { get; set; } } }

De lo anterior se puede resaltar lo siguiente: • La inclusión del nombre de espacio DataAnnotations.

CAPÍTULO CUATRO – CARACTERÍSTICAS AVANZADAS

40

• El atributo sobre las propiedades de clase Required el cual permite definir que sea un campo requerido, así jQuery validate podrá colocarlo como requerido. Incluye la propiedad ErrorMessage, el cual permite agregar un mensaje personalizado en caso de que no se cumpla la condición indicada. • El atributo Display que permite cambiar la forma en que en un label se mostrará el nombre del campo. • El atributo MaxLength es uno de muchos otros atributos que se pueden agregar a propiedades de clase para definir características. 2. Para poder probar una validación debe primero existir un formulario de ingreso de datos. Para esto cree dos acciones en el controlador ItemsController. Uno para retornar el HTML y otro para recibir el submit del formulario. De la siguiente manera. 1 2 3 4 5 6 7 8 9 10

public IActionResult Crear() { return View(); } [HttpPost] [ValidateAntiForgeryToken] public IActionResult Crear(Item nuevoItem) { return View(nuevoItem); }

De lo anterior se puede resaltar lo siguiente: • Dos acciones, una para retornar el formulario vacío y otro para recibir los datos del submit. • El atributo [ValidateAntiForgeryToken] el cual permite evitar ataques de CSRF. Debería ser utilizado siempre en las solicitudes de tipo POST o PUT.

CAPÍTULO CUATRO – CARACTERÍSTICAS AVANZADAS

41

• Un retorno en el POST de la sentencia View() con el modelo que ingresó por la solicitud, es decir, se retorna el formulario con la información previamente ingresada. 3. Agregar la información del formulario HTML 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

@model Item Sub\ mit @section Scripts { @{await Html.RenderPartialAsync("_ValidationScript\ sPartial"); } }

De lo anterior se puede resaltar lo siguiente: • El formulario con el asp-action y asp-controller como

CAPÍTULO CUATRO – CARACTERÍSTICAS AVANZADAS

42

ya se mencionó anteriormente para indicar cual extremo contactar. • El atributo asp-anti-forgery para que el formulario sea compatible con el atributo anteriormente agregado [ValidateAntiForgeryToken] y así evitar ataques de CSRF. • El @model asignado es de tipo Item, esto le da el contexto al formulario para saber que será un formulario para ese tipo de dato. • El atributo asp-validation-summary=”ValidationSummary.All” el cual si existen errores en el formulario generará un tipo resumen de todos los problemas. • El atributo asp-for en labels e inputs, el cual extrae los metadatos agregados a través de las propiedades de clases y DataAnnotations y los renderiza, por ejemplo, asigna el tipo de dato del input, el label, y mensajes de error. • La sección Scripts, que permite renderizar los scripts de jQuery Validate que interceptan un submit y validan de lado cliente un formulario. 4. Agregue un breakpoint en el action que recibe la solicitud, es decir, el que tiene le atributo HttpPost y corra el proyecto (F5). En el formulario, sin llenar ningún campo de clic en submit.

CAPÍTULO CUATRO – CARACTERÍSTICAS AVANZADAS

43

5. Ingrese valores que cumplan con las especificaciones de validación y realice el submit. Debería llegar al breakpoint con los datos ingresados. 6. Lo que se acaba de realizar es validación de lado cliente, la cual puede extenderse del lado servidor con la siguiente sentencia en la acción post del crear. 1 2 3 4 5 6 7 8 9 10

[HttpPost] [ValidateAntiForgeryToken] public IActionResult Crear(Item nuevoItem) { if (ModelState.IsValid) { // guardar en la base de datos return RedirectToAction("Index"); } return View(nuevoItem);

CAPÍTULO CUATRO – CARACTERÍSTICAS AVANZADAS

11

44

}

7. La variable ModelState permite validar el estado de lo que se haya utilizado como contexto del formulario. Si los datos se ingresan correctamente, el ModelState.IsValid será true, de caso contrario será un false.

Vistas parciales vs ViewComponents (VCs) Los VCs son muy similares a lo que en versiones anteriores se conocían como Vistas parciales, las cuales buscan renderizar pequeños bloques de HTML, sirve en casos en los que se requiera manipular pequeñas porciones de una página, por ejemplo con manejo asincrónico de una página. La principal diferencia es que un ViewComponent es mucho más poderoso que una Vista Parcial debido a las formas de manipular la comunicación. Ejemplo de Vista Parcial 1. Crear una vista en el folder Views/Shared que será invocada desde el _Layout.cshtml

CAPÍTULO CUATRO – CARACTERÍSTICAS AVANZADAS

45

2. Reemplace todo el contenido por el contenido a continuación (el css se agrega en el mismo archivo solamente por simplicidad, no es una buena práctica bajo ninguna razón) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

Noticias Costa Rica en el puesto #13 de FIFA Est porta ac magna lundium? Amet eros. Lore\ m cum ut auctor vel integer mus tortor, adipiscing plat\ ea penatibus, in placerat, lectus adipiscing! Ultrices \ scelerisque adipiscing parturient! Nuevo ASP.NET 5 Est porta ac magna lundium? Amet eros. Lore\ m cum ut auctor vel integer mus tortor, adipiscing plat\ ea penatibus, in placerat, lectus adipiscing! Ultrices \ scelerisque adipiscing parturient!

CAPÍTULO CUATRO – CARACTERÍSTICAS AVANZADAS

16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41

46

¡Hololens está aquí! Est porta ac magna lundium? Amet eros. Lore\ m cum ut auctor vel integer mus tortor, adipiscing plat\ ea penatibus, in placerat, lectus adipiscing! Ultrices \ scelerisque adipiscing parturient! .bannerNoticias { border:solid 1px #aaa; border-radius:10px; padding:12px; } .bannerNoticias .noticias { display:inline-block; float:left; max-width:200px; font-size:12px; } .clearer{ clear:both; }

3. En el archivo de Layout, inserte la siguiente línea de código debajo de la sentencia @RenderBody 1 2

@RenderBody() @await Html.PartialAsync("_bannerNoticias")

4. Ejecute el proyecto (F5) y podrá apreciar una Vista parcial incrustada en cada página del sitio.

CAPÍTULO CUATRO – CARACTERÍSTICAS AVANZADAS

47

Ejemplo de ViewComponent En este caso crearemos un viewcomponent que logre lo mismo que la vista parcial anterior, sin embargo esta vez obtendremos las noticias desde el código, lo que más adelante podría ser reemplazado por datos desde una base de datos. 1. Crear un folder llamado ViewComponents en la raíz del proyecto. 2. Cree una clase llamada BannerInteractivoViewComponent.cs 3. Reemplace el contenido de la clase por el siguiente 1 2 3 4 5 6 7 8 9 10 11

using Microsoft.AspNet.Mvc; using System.Collections.Generic; using System.Linq; namespace .ViewComponents { public class BannerInteractivoViewComponent : V\ iewComponent { Dictionary noticias = new\ Dictionary();

CAPÍTULO CUATRO – CARACTERÍSTICAS AVANZADAS

12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37

48

public IViewComponentResult Invoke() { noticias = new Dictionary() { { "Costa Rica en el puesto #13 de F\ IFA","Est porta ac magna lundium? Amet eros. Lorem cum \ ut auctor vel integer mus tortor, adipiscing platea pen\ atibus, in placerat, lectus adipiscing! Ultrices sceler\ isque adipiscing parturient!"}, { "Nuevo ASP.NET 5","Est porta ac m\ agna lundium? Amet eros. Lorem cum ut auctor vel intege\ r mus tortor, adipiscing platea penatibus, in placerat,\ lectus adipiscing! Ultrices scelerisque adipiscing par\ turient!"}, { "¡Hololens está aquí!","Est porta\ ac magna lundium? Amet eros. Lorem cum ut auctor vel i\ nteger mus tortor, adipiscing platea penatibus, in plac\ erat, lectus adipiscing! Ultrices scelerisque adipiscin\ g parturient!"} }; return View(noticias); } } }

1. Ahora, cree un folder en la dirección Views/Shared/Components y dentro de ese folder, cree otro llamado BannerInteractivo (Views/Shared/Components/BannerInteractivo) y luego cree una vista que se llame Default.cshtml, agrege el siguiente contenido a la vista (el css se agrega en el mismo archivo solamente por simplicidad, no es una buena práctica bajo ninguna razón).

CAPÍTULO CUATRO – CARACTERÍSTICAS AVANZADAS

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

49

@model Dictionary Noticias @foreach (var item in Model) { @item.Key @item.Value } .bannerNoticias { border: solid 1px #aaa; border-radius: 10px; padding: 12px; } .bannerNoticias .noticias { display: inline-block; float: left; max-width: 200px; font-size: 12px; } .clearer { clear: both; }

2. Si ejecuta el proyecto (F5) verá el mismo resultado 2 veces, sin embargo con el ViewComponent podrá sacar los datos desde una base de datos más adelante, e inclusive ejecutarlo de manera asincrónica.

CAPÍTULO CUATRO – CARACTERÍSTICAS AVANZADAS

50

Agregando una base de datos Para esto utilizaremos Entity Framework Code First y obtendremos la información de las noticias desde la base de datos. Debido a esto, usaremos la instancia de LocalDB existente. 1. Crearemos una entidad de noticia para este ejemplo, en Models cree una clase que se llame Noticia.cs, reemplace el contenido con el siguiente 1 2 3 4 5 6 7 8 9 10 11 12 13 14

using using using using

System; System.Collections.Generic; System.Linq; System.Threading.Tasks;

namespace .ui.Models { public class Noticia { public int Id { get; set; } public string Titulo { get; set; } public string Descripcion { get; set; } } }

1. Primero agregue las dependencias de Entity Framework a través de NuGet al proyecto

CAPÍTULO CUATRO – CARACTERÍSTICAS AVANZADAS

51

2. Cuando finalice la instalación, vamos a proceder a agregar el contexto de Code First. En el mismo folder de Models, cree un archivo que se llame NoticiasAppContext.cs y reemplace el contenido con el siguiente.

CAPÍTULO CUATRO – CARACTERÍSTICAS AVANZADAS

1 2 3 4 5 6 7 8 9

52

using Microsoft.Data.Entity; namespace wm.website.ui.Models { public class NoticiasAppContext : DbContext { public DbSet Noticias { get; set; } } }

3. Con esto listo, se debe configurar Entity Framework (registrar la dependencia) y agregar la conexión a la base de datos. Esto se realiza a través del archivo Startup.cs. en el método ConfigureServices se agregan las configuraciones, encontrará que ya se está agregando Entity Framework y configurado un contexto, este contexto es el que ya se usa para autenticación de usuarios. Pero debemos agregar el nuevo contexto para las Noticias. 1 2 3 4 5 6 7 8 9 10 11

// Add EF services to the services container. services.AddEntityFramework() .AddSqlServer() .AddDbContext(op\ tions => options.UseSqlServer(Configuration\ ["Data:DefaultConnection:ConnectionString"])) .AddDbContext(opti\ ons => options.UseSqlServer(Configuration\ ["EntityFramework:ConnectionString"]));

4. Como ve en la sección de Configuration de este Nuevo DbContext hay un string, este es el que indica a cual base de datos conectarse. Esta información se extrae del archivo config.json. Este archivo ya cuenta con un connection string, utilizado para la administración de usuarios fuera de caja.

CAPÍTULO CUATRO – CARACTERÍSTICAS AVANZADAS

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

53

{ "AppSettings": { "SiteTitle": "wm.website.ui" }, "Data": { "DefaultConnection": { "ConnectionString": "Server=(localdb)\\mss\ qllocaldb;Database=aspnet5-wm.website.ui-a913355d-72f6-\ 4d80-892a-19699864e285;Trusted_Connection=True;Multiple\ ActiveResultSets=true" } }, "EntityFramework": { "ConnectionString": "Data Source=INSTANCIA_SQL\ SERVER;Integrated Security=True;Connect Timeout=15;Encr\ ypt=False;TrustServerCertificate=False;ApplicationInten\ t=ReadWrite;MultiSubnetFailover=False" } }

5. Con esto listo, proceda a compilar el proyecto. 6. Volveremos al ViewComponent de Noticias y reemplazaremos las noticias locales por unas salidas de base de datos. En el archivo ViewComponentes/BannerInteractivoViewComponent.cs. 1 2 3 4 5 6 7 8 9

using using using using

Microsoft.AspNet.Mvc; System.Collections.Generic; System.Linq; wm.website.ui.Models;

namespace .ui.ViewComponents { public class BannerInteractivoViewComponent : View\ Component

CAPÍTULO CUATRO – CARACTERÍSTICAS AVANZADAS

10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44

54

{ List noticias = new List(); NoticiasAppContext db; public BannerInteractivoViewComponent(Noticias\ AppContext _context) { db = _context; } public IViewComponentResult Invoke() { // Limpiemos la base de datos if (db.Noticias.Count() > 0) db.Noticias.RemoveRange(db.Noticias); db.SaveChanges(); // Recreemos los 3 archivos db.Noticias.AddRange(new List() { new Noticia() { Id=1, Titulo="Costa Ri\ ca en el puesto #13 de FIFA", Descripcion="Est porta ac\ magna lundium? Amet eros. Lorem cum ut auctor vel inte\ ger mus tortor, adipiscing platea penatibus, in placera\ t, lectus adipiscing! Ultrices scelerisque adipiscing p\ arturient!" }, new Noticia() { Id=1, Titulo="Nuevo AS\ P.NET 5", Descripcion="Est porta ac magna lundium? Amet\ eros. Lorem cum ut auctor vel integer mus tortor, adip\ iscing platea penatibus, in placerat, lectus adipiscing\ ! Ultrices scelerisque adipiscing parturient!" }, new Noticia() { Id=1, Titulo="¡Hololen\ s está aquí!", Descripcion="Est porta ac magna lundium?\ Amet eros. Lorem cum ut auctor vel integer mus tortor,\ adipiscing platea penatibus, in placerat, lectus adipi\ scing! Ultrices scelerisque adipiscing parturient!" } });

CAPÍTULO CUATRO – CARACTERÍSTICAS AVANZADAS

45 46 47 48 49 50

55

db.SaveChanges(); return View(db.Noticias.ToList()); } } }

7. Al concretar este punto, se podrán ver las noticias pero saliendo de una base de datos SQL Server.

CONCLUSIÓN Debido a que este libro fue escrito con una versión de Visual Studio que aún no es RTM y con varios componentes que aún están en preview, estaré tratando de actualizarlo en la siguiente dirección ASP.NET MVC 6 - UNA GUÍA INTRODUCTORIA⁶. Dependiendo del éxito de este libro, estaré tratando de sacar otros más avanzados y de los cuales tengo algunos temas en mente, tales como características avanzadas en MVC, aplicaciones MVC 6 con AngularJS, Web API avanzado, Microsoft Azure, entre otros. Así que agradezco si pudieran brindarme feedback de este, mi primer libro técnico. Gracias a su feedback estaré enviándoles a su correo electrónico cuando saque actualizaciones de esta serie u otros libros de interés que genere en español. Contacto Email: [email protected]⁷ Twitter: @tewar93⁸ http://waltermontes.com⁹

⁶http://waltermontes.com ⁷mailto:[email protected] ⁸https://twitter.com/tewar93 ⁹http://waltermontes.com

View more...

Comments

Copyright ©2017 KUPDF Inc.
SUPPORT KUPDF