Una sinfonía en C#

Un pequeño aporte a la comunidad de habla hispana.

Taller sobre MVC3 en Buenos Aires

mvc_DesignPattern

Durante las primeras semanas de Abril estaré en el Grupo de Usuarios de Microsoft dando un nuevo taller sobre MVC, en este caso actualizado a la versión 3 y hablando un poco sobre las novedades de MVC 4.

Contenido

Por supuesto que debido a la cantidad de temas y la duración del taller el nivel será introductorio, la idea es dar un repaso sobre los conceptos más sobresalientes y no dejar de nombrar algunos detalles de implementación en situaciones reales, el temario será el siguiente:

Introducción a MVC

  • Teoría
  • Separación de conceptos
  • Escalabilidad
  • Implementaciones: ASP.NET MVC, Monorail.
  • Diferencias entre MVC y WebForms / ASP clásico
  • Colaboración
  • Legibilidad
  • Testeo
  • Por qué necesitamos MVC?

Ruteo

  • Modelos de ruteo
  • SEO
  • Indización de buscadores
  • Configuración personalizada
  • Áreas

El modelo

  • Responsabilidad del modelo
  • Separación
  • Testeo

El controlador

  • Responsabilidades
  • Action Filters
  • Model Binders
  • ActionResult
  • FileResult
  • JSONResult
  • JavaScriptResult

La vista

  • Responsabilidad
  • Motores de renderización
    • ASP.NET
    • Razor
  • HTMLHelpers
  • ViewData
  • Vistas tipadas
  • Validaciones
  • Testeo

Aspectos avanzados

  • Ajax
  • jQuery y MVC
  • MVC 4
  • Mejoras en Razor
  • ASP.NET Web API

El taller se desarrollará íntegramente con PC para cada asistente para poder experimentar con los conceptos que vamos repasando.

Fechas y duración

Serán cuatro encuentros:

  • Lunes 16 de Abril de 2012
  • Martes 17
  • Lunes 23
  • Martes 24

el horarios será de 18:00 a 22:00 hs. El lugar es en Rivadavia 1479 - Piso 1 - Oficina A 
Ciudad Autónoma de Bs.As.

Registración

Dejo el link para registrarse, el cupo es limitado y es con costo.

Nos vemos, Leonardo.

Namespaces en aplicaciones Metro Javascript con la ayuda de WinJS

En el post anterior sobre Metro vimos cómo se puede hacer para “bindear” el modelo a la vista, existe una particularidad en el ejemplo que pasó casi inadvertida y tiene que ver con la forma de organizar las aplicaciones Metro Javascript.

El patrón module en Javascript

En este post vimos cómo se pueden utilizar funciones auto-ejecutable para tener un ámbito de visibilidad limitado y que nuestro código no interfiera con el resto de la aplicación, la utilización de esta técnica se conoce como el patrón module.

El inconveniente de utilizarlo es que lo que definamos dentro de la función no podrás ser visto desde afuera, es por eso que yo definí el modelo como una propiedad del objeto document el cual es visible globalmente, sin embargo, existe una alternativa a esto.

Namespaces en Metro

Podemos crear un objeto global y generar un espacio de nombres y allí poner los orígenes de datos, ya vimos cómo podemos hacerlo, sin embargo WinJS ya trae consigo un mecanismo para crear espacios de nombre.

La idea es definir dentro de un módulo nuestro origen de datos pero dentro de un espacio de nombres que pueda ser visto globalmente para poder referenciarlo desde la vista con su nombre completo y el WinJS se ocupe del binding. Entonces manos a la obra>

Un ejemplo vale más de mil palabras

Creamos una nueva aplicación Metro de Javascript y vamos directo al archivo default.js

// For an introduction to the Blank template, see the following documentation:
// http://go.microsoft.com/fwlink/?LinkId=232509
(function () {
    "use strict";

    var app = WinJS.Application;

    app.onactivated = function (eventObject) {
        if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {
            if (eventObject.detail.previousExecutionState !== Windows.ApplicationModel.Activation.ApplicationExecutionState.terminated) {
                // TODO: This application has been newly launched. Initialize 
                // your application here.
            } else {
                // TODO: This application has been reactivated from suspension. 
                // Restore application state here.
            }
            WinJS.UI.processAll();
        }
    };

    app.oncheckpoint = function (eventObject) {
        // TODO: This application is about to be suspended. Save any state
        // that needs to persist across suspensions here. You might use the 
        // WinJS.Application.sessionState object, which is automatically
        // saved and restored across suspension. If you need to complete an
        // asynchronous operation before your application is suspended, call
        // eventObject.setPromise(). 
    };

    app.start();
})();

Inicialmente tenemos esto, vamos a generar un origen de datos y colocarlo como propiedad del objeto document

(function () {
    "use strict";
    //creamos el origen de datos
    var dataSource = { nombre: 'leonardo', apellido: 'micheloni' };
    //lo asignamos como una propiedad del document para tener visibilidad fuera de
    //este módulo
    document.dataSource = dataSource;

    var app = WinJS.Application;

    app.onactivated = function (eventObject) {
        //le decimos a WinJS que ejecute los binding
        WinJS.Binding.processAll();
    };

    app.start();
})();

Modificamos el HTML

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Application16</title>

    <!-- WinJS references -->
    <link href="//Microsoft.WinJS.0.6/css/ui-dark.css" rel="stylesheet">
    <script src="//Microsoft.WinJS.0.6/js/base.js"></script>
    <script src="//Microsoft.WinJS.0.6/js/ui.js"></script>

    <!-- Application16 references -->
    <link href="/css/default.css" rel="stylesheet">
    <script src="/js/default.js"></script>
</head>
<body>
    <div data-win-bindsource="document.dataSource">
        <p data-win-bind="innerText: nombre"></p>
        <p  data-win-bind="innerText: apellido"></p>
    </div>
</body>
</html>

y funciona muy bien

image

Utilizando la función namespace de WinJS

Ahora, vamos a poner nuestro origen de datos dentro de otro archivo js y agregarlo a un namespace y luego referenciarlo desde la vista

default.js

(function () {
    "use strict";
    var app = WinJS.Application;

    app.onactivated = function (eventObject) {
        //le decimos a WinJS que ejecute los binding
        WinJS.Binding.processAll();
    };
    
    app.start();
})();

data.js

(function () {
    'use strict';
    //creamos el origen de datos
    var dataSource = { nombre: 'leonardo', apellido: 'micheloni' };
    //definimos el namespace "datos" y colocamos nuestro origen dentro
    WinJS.Namespace.define("datos", dataSource);
})();

default.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Application16</title>

    <!-- WinJS references -->
    <link href="//Microsoft.WinJS.0.6/css/ui-dark.css" rel="stylesheet">
    <script src="//Microsoft.WinJS.0.6/js/base.js"></script>
    <script src="//Microsoft.WinJS.0.6/js/ui.js"></script>

    <link href="/css/default.css" rel="stylesheet">
    <script src="/js/default.js"></script>
    <!-- referenciamos el archivo donde definimos nuestro origen de datos-->
    <script src="/js/data.js"></script>
</head>
<body>
    <div data-win-bindsource="document.dataSource">
        <p data-win-bind="innerText: nombre"></p>
        <p  data-win-bind="innerText: apellido"></p>
    </div>
</body>
</html>

y el resultado es igual al anterior

image

De más está decir que son pocas las situaciones en la cuales no deberíamos colocar nuestro código en un módulo separado y referenciarlo utilizando namespaces.

Hasta la próxima, Leonardo.

Cómo crear espacios de nombres en Javascript

Si bien Javascript no provee un mecanismo propio para la creación de espacios de nombres es posible lograr este tipo de organización de nuestro código con algunas técnicas.

Primer intento

Podemos crear un objeto y utilizarlo como raíz del namespace y agregar los objetos que queramos dentro del namespace como propiedades de la raíz.

<html>
<head>
<script type="text/javascript">
	//creamos nuestro namespace
	var MiEspacio = {};
	//agregamos un objeto "dentro" del namespace
	MiEspacio.Objeto1 = function(){};
	//entonces podemos hacer
	MiEspacio.Objeto1.propiedad1 = "hola";
	//y comprobamos que se pueda leer la propiedad
	console.log(MiEspacio.Objeto1.propiedad1);
</script>
</head>
<body></body>
</html>

podemos repetir esto para tener un namespace con la cantidad de niveles que nos sea necesario

<html>
<head>
<script type="text/javascript">
	//creamos nuestro namespace
	var MiEspacio = {};
	//agregamos un objeto "dentro" del namespace y del módulo
	MiEspacio.modulo1.Objeto1 = function(){};
	//entonces podemos hacer
	MiEspacio.modulo1.Objeto1.propiedad1 = "hola";
	//y comprobamos que se pueda leer la propiedad
	console.log(MiEspacio.modulo1.Objeto1.propiedad1);
</script>
</head>
<body></body>
</html>

por supuesto que para tenet una visibilidad alta deberíamos definir el objeto raíz lo más global posible.

Creando una función para generar namespaces

Vamos a crear una función que nos permita generar namespaces de una manera sencilla y mejor aún, que verifique que no se creen dos veces los mismo, o peor aún, que se sobre-escriban elementos ya definidos.

La idea es hacer algo así:

UTIL = {}; //definimos el objeto UTIL
RAIZ = {}; //definimos el objeto raíz
UTIL.namespace = function (expression, members){
		//dividimos la expresion en cada punto 
		var partes = expression.split('.');
		//el objeto raíz es el primer elemento
		var raiz = eval(partes[0]);
		//quitamos de la expresion la raíz
		partes = partes.slice(1);
		for(i = 0;i < partes.length;i++){		
			//en caso que no está ya definido es módulo
			//se crea
			if(typeof raiz[partes[i]] === 'undefined'){
				//es el último elemento agregamos los miembros
				if(i==partes.length-1){
					raiz[partes[i]] = members;
				}else{
					raiz[partes[i]] = {};
				}
			}
		}
};

UTIL.namespace("RAIZ.pp", {objeto1: {propiedad1: "valor"}});

y listo

image

por supuesto que esta función es muy mejorable, por ejemplo podría crear el objeto raíz si no existe, pero sirve de ejemplo.

Nos leemos.

Leonardo.

Data bindings en aplicaciones Metro con Javascript

Las aplicaciones metro con Javascript utilizar al patron MVVM, WinJS se encarga de gestionar el funcionamiento del mismo.

Model View-View Model

La idea de este patrón es tener un modelo asociado a una vista de la aplicación, es decir: si tenemos una vista que muestra un listado de productos ese listado es el modelo de la vista, cuando para nuestro dominio el producto es parte del modelo y el listado no existe como tal.

Algo adicional es que la asociación del modelo de la vista con los elementos de la vista (en este caso elementos HTML) es automática y no tenemos que poner sintaxis tipo MVC y llamadas a helpers para que se muestren los datos sino que es un mecanismo más transparente

Atributos data-algo

HTML5 permite definir como válidos atributos para cualquier elemento con el nombre data-nombre-subnombre para utilizarlos de manera personalizada, justamente WinJS se basa en esto para asociar el modelo con la la vista.

Manos a la obra

Vamos a hacer una aplicación Metro bien sencilla para demostrar cómo funciona esto, vamos al Visual Studio y creamos una aplicación Metro Javascript en blanco.

image

La aplicación es bien simple

Vamos al archivo default.js y vemos lo siguiente

// For an introduction to the Blank template, see the following documentation:
// http://go.microsoft.com/fwlink/?LinkId=232509
(function () {
    "use strict";

    var app = WinJS.Application;

    app.onactivated = function (eventObject) {
        if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {
            if (eventObject.detail.previousExecutionState !== Windows.ApplicationModel.Activation.ApplicationExecutionState.terminated) {
                // TODO: This application has been newly launched. Initialize 
                // your application here.
            } else {
                // TODO: This application has been reactivated from suspension. 
                // Restore application state here.
            }
            WinJS.UI.processAll();
        }
    };

    app.oncheckpoint = function (eventObject) {
        // TODO: This application is about to be suspended. Save any state
        // that needs to persist across suspensions here. You might use the 
        // WinJS.Application.sessionState object, which is automatically
        // saved and restored across suspension. If you need to complete an
        // asynchronous operation before your application is suspended, call
        // eventObject.setPromise(). 
    };

    app.start();
})();

En principio este js no tiene más que los eventos de la aplicación asociados, vamos a modificar levemente el código para crear un objeto que será nuestro modelo y pasarlo a la vista

(function () {
    "use strict";

    var app = WinJS.Application;

    //colocamos nuestro código para que se ejecute cuando la aplicación se activa
    app.onactivated = function (eventObject) {
        //creamos nuestro modelo
        var modelo = { nombre: "leonardo", apellido: "micheloni" };
        //le decimos a WinJS que procese los bindings y le especificamos cuál
        //el primer parámetro es el objeto DOM desde el cual se ejecuta el binding
        //en caso de poner null se utiliza document
        //el segundo parámetro es el modelo
        WinJS.Binding.processAll(null, modelo); 
    };

    //iniciamos la aplicación
    app.start();
})();

con eso alcanza, quité el código que no va a hacer falta, y agregué dos líneas, en la primer defino un objeto que será el modelo, con WinJS.Binding.processAll ejecutamos los bindings, ahora vamos a modificar la vista, que en principio es así:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>ejemploBinding</title>

    <!-- WinJS references -->
    <link href="//Microsoft.WinJS.0.6/css/ui-dark.css" rel="stylesheet">
    <script src="//Microsoft.WinJS.0.6/js/base.js"></script>
    <script src="//Microsoft.WinJS.0.6/js/ui.js"></script>

    <!-- ejemploBinding references -->
    <link href="/css/default.css" rel="stylesheet">
    <script src="/js/default.js"></script>
</head>
<body>
    <p>Content goes here</p>
</body>
</html>

la modificamos para que quede así:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>ejemploBinding</title>

    <!-- WinJS references -->
    <link href="//Microsoft.WinJS.0.6/css/ui-dark.css" rel="stylesheet">
    <script src="//Microsoft.WinJS.0.6/js/base.js"></script>
    <script src="//Microsoft.WinJS.0.6/js/ui.js"></script>

    <!-- ejemploBinding references -->
    <link href="/css/default.css" rel="stylesheet">
    <script src="/js/default.js"></script>
</head>
<body>
    <div>
        <p data-win-bind="innerText: nombre"></p>
        <p data-win-bind="innerText: apellido"></p>
    </div>
</body>
</html>

la propiedad data-win-bind permite indicar una propiedad del elemento HTML (en este caso innerText) y asignarlo un valor proveniente desde el modelo, en este caso nombre, el resultado

image

Un poco más declarativo

Podemos hacer lo mismo sin necesidad de indicar por código la asociación del modelo con la vista, modificamos primero el js

(function () {
    "use strict";

    var app = WinJS.Application;
    //colocamos nuestro modelo en el document, para poder verlo globalmente
    document.modelo = { nombre: "leonardo", apellido: "micheloni" };

    app.onactivated = function (eventObject) {
        WinJS.Binding.processAll(); 
    };

    app.start();
})();

Simplemente declaramos el modelo como una propiedad del objeto document, no es lo más feliz tal vez sin embargo sin usar namespaces (que será tema de otro post) es lo que tenemos más a mano. Vamos a ver la vista.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>ejemploBinding</title>

    <!-- WinJS references -->
    <link href="//Microsoft.WinJS.0.6/css/ui-dark.css" rel="stylesheet">
    <script src="//Microsoft.WinJS.0.6/js/base.js"></script>
    <script src="//Microsoft.WinJS.0.6/js/ui.js"></script>

    <!-- ejemploBinding references -->
    <link href="/css/default.css" rel="stylesheet">
    <script src="/js/default.js"></script>
</head>
<body>
    <div data-win-bindsource="document.modelo" >
        <p data-win-bind="innerText: nombre"></p>
        <p data-win-bind="innerText: apellido"></p>
    </div>
</body>
</html>

nótese que en declaramos el data-win-bindsource en el div que contiene a los párrafos, esta es otro de las propiedades de WinJS para manipular los datos de la vista.

Lo ejecutamos y obtenemos el mismo resultado

image

Mágico, nos leemos.

Leonardo.

Cómo subir archivos con ASP.NET MVC + AJAX

Ya hemos hablado acerca de cómo subir archivos con ASP.NET MVC y algunos detalles sobre formularios y demás, en esta oportunidad vamos a ver cómo podemos lograr lo mismo pero usando AJAX.

Primero el lado del cliente

Del lado del cliente podemos acceder al objeto File controlado por el usuario, simplemente haciendo esto (usando jQuery claro)

$(function () {
    var file = $("#file1");
}); 

Uno pensaría (como lo hacía yo hasta éste momento) que no es posible acceder a un archivo desde Javascript, sin embargo el sandbox no nos impide acceder a un objeto creado y manejado por el usuario, por lo tanto podemos acceder al contenido del archivos una vez que el usuario lo haya seleccionado, modificamos el código para tal fin

<!DOCTYPE html>

<html>
<head>
    <title>Index</title>
    <script src="@Url.Content("~/Scripts/jquery-1.5.1.js")" type="text/javascript"></script>
    <script type="text/javascript">
        $(function () {
            $("#frm :submit").click(function () {
                var file = $("#file1").get();
                return false;
            });
        });    
    </script>
</head>
<body>
    <div>
        <form id="frm" action="@Url.Action("Upload")" method="post">
            <input type="file" id="file1" />
            <input type="submit" value="enviar" />
        </form>
    </div>
</body>
</html>

Entonces atrapamos el evento click del botón submit del formulario para que cuando se ejecute recuperar el objeto File, retornamos false para que no se ejecute el evento por defecto, es decir, que no se envíe el formulario.

Enviando el contenido del objeto File por Ajax

Para enviar el contenido del File por Ajax vamos a tener que utilizar el la función AJAX de jQuery directamente ya que necesitamos más control que el que nos brinda $.post. para lograrlo modificamos el código nuevamente.

        $(function () {
            $("#frm :submit").click(function () {
                var file = $("#file1").get();
                $.ajax({
                    type: 'POST',
                    url: $("#frm").attr("action"),
                    data: file,
                    dataType: "application/json",
                    contentType: "application/octet-stream"
                });
                return false;
            });
        });

nótese que ponemos en el Content-Type el tipo octet-stream para poder enviar el archivo como un stream y leerlo fácilmente desde C#

Leyendo el contenido desde ASP.NET MVC

Lo que hacemos es enviar por AJAX utilizando POST el contenido del objeto File, esto está muy bien, vamos a ver cómo lo recibimos desde el lado de ASP.NET MVC

using System.Web.Mvc;
using System.IO;
using System.Text;

namespace MvcApplication6.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult Upload()
        {
            var file = new StreamReader(this.HttpContext.Request.InputStream, Encoding.UTF8).ReadToEnd();
            return Json(new { contenidoRecibido = file });
        }
    }
}

Para leer el archivo enviado tenemos que acceder el InputStream ya que estamos enviando el contenido como parte del cuerpo de HTTP. Un detalle más es que por defecto viaja con Encoding UTF-8 es por eso que para leerlo tenemos que especificarlo en el StreamReader. Por supuesto que este ejemplo sirve sólo para archivos de texto, no es difícil modificarlo para cualquier tipo de contenido.

image

y listo, faltaría poner código del lado del cliente para que ante la respuesta correcta (no la que puse que es un ejemplo) de feedback al usuario.

Nos leemos, Leonardo.