DNM+ Online
dotnetmania 2.0
ASP.NET MVC y jQuery. Más poder, más control, ¿más esfuerzo?
En los últimos años, la Web ha evolucionado considerablemente para crear páginas más dinámicas, más colaborativas y más dedicadas al usuario. En este artículo se verá cómo combinar ASP.NET MVC y jQuery para producir una interfaz de usuario más responsiva y de mayor calidad.

La llamada Web 2.0 se caracteriza por ser más dinámica, permitiendo una mayor interacción con los usuarios. JavaScript ha jugado un papel protagónico para lograr aplicaciones con estas características y a la vez seguir cumpliendo con los estándares web. Muchos lectores tendrán conciencia del grado de complejidad que tiene desarrollar este tipo de aplicaciones. Las dificultades se deben fundamentalmente a la arquitectura cliente-servidor y desconectada del protocolo HTTP, la cual obliga a una división en el desarrollo: por un lado, el código que se ejecuta en el servidor, y por otro, aquel que se ejecuta en el navegador una vez que se carga la página web. Ambas partes tienen que estar en sintonía para producir una aplicación que contenga una interfaz de usuario funcional y agradable (al estilo de las aplicaciones de escritorio) junto a un comportamiento (usualmente guiado por el servidor) que esconda esta complejidad del protocolo HTTP.

Por esta razón han surgido las RIA1 1 tales como Adobe Flex y Microsoft Silverlight. El problema con las RIA radica en que cada una exige instalar componentes adicionales en el navegador para poder ejecutar la aplicación (por ejemplo, Flash o Silverlight). Si bien internamente esto puede ser aceptable para una organización, en el vasto mundo de Internet (en ocasiones intolerante) esto puede resultar molesto o inviable para un determinado tipo de usuarios, sobre todo si metemos en escena a las luchas comerciales y otros intereses que no son propiamente tecnológicos.

Esto ha llevado a que muchos desarrolladores decidan "machacarse" y volver a lo básico: Java­Script. JavaScript es pequeño y limitado en comparación con los lenguajes de propósito general, pero es suficiente para el desarrollo de aplicaciones web interesantes gracias a la disponibilidad de un conjunto de bibliotecas de funciones (tal es el caso de jQuery [2]) que incluyen mucho código de fácil utilización para desarrollar interfaces de usuario de calidad y que ejecutan en cualquier navegador web o sistema operativo.

Pero para utilizar jQuery (o cualquier otra biblioteca de propósito similar, como Prototype) más allá de la creación de animaciones (DHTML) o modificación de los elementos de la página web con el objetivo de crear una presentación más estilizada, por ejemplo, para crear controles que extraigan datos del servidor o que dependan de cierto comportamiento definido en esa parte, juega un papel fundamental la plataforma que esté del lado del servidor, ya que en dependencia de ésta, la integración con jQuery será más simple o más enrevesada.

Como posiblemente ya el lector habrá escuchado, ASP.NET MVC [3] implica un cambio radical para los desarrolladores web que utilizan tecnología .NET debido a que su diseño se basa en una arquitectura limpia, fundamentada en la inclusión de patrones de diseño para agilizar el desarrollo y en la aplicación de pruebas unitarias para garantizar la calidad del software. En este artículo se verá cómo jQuery y ASP.NET MVC pueden combinarse de manera natural para desarrollar aplicaciones de alta calidad. Note que esto no niega que jQuery se pueda utilizar con ASP.NET clásico; sino que muchos de los problemas que los desarrolladores pueden encontrarse con ASP.NET clásico (tales como la creación de manejadores HTTP para servir datos y la conversión manual a JSON2) se simplifican con la forma de desarrollo que introduce ASP.NET MVC.

Desarrollando un control "Selección de persona"

Para mostrar la integración entre jQuery y ASP.NET MVC se escogió como ejemplo el desarrollo de un control3 para "seleccionar una persona". Este control pudiera implementarse como una caja de texto común y corriente que reciba una cadena de texto, pero en tal caso tendríamos el problema de la libertad del formato: el nombre "Angelina Jolie" es, desde el punto de vista computacional, diferente a "A. Jolie" o "Jolie, Angelina"; pero semánticamente es la misma persona. Por tanto, se requiere el desarrollo de un control que cumpla los siguientes requisitos:

  • Restrinja el formato para que se identifique a una misma persona de manera única.
  • Ayude al usuario a realizar la selección por si éste no recuerda el nombre completo.
  • Minimice el tráfico cliente-servidor, pero que permita la búsqueda sobre una base de datos de personal que puede ser voluminosa.

Note que los controles predeterminados de HTML no satisfacen estos requerimientos. Por ejemplo, una primera idea podría ser utilizar un menú desplegable (Dropdown List), pero este tipo de control requiere que se "rellenen" las opciones de selección a priori, por lo que sería inmanejable para muchos elementos (tanto por el tráfico como por el gasto de memoria en el navegador web).

La solución que vamos a ilustrar consiste en utilizar una caja de texto que autocomplete los nombres de manera eficiente a partir de una lista de personas, y que el texto que se escriba en la caja de texto se valide contra dicha lista.

Separando las responsabilidades

En ASP.NET clásico, una página web dinámica (.aspx) puede verse como un gran proceso monolítico que construye lo que será la página HTML que visualiza el navegador web, pero manteniendo el estado de ejecución de la lógica de la aplicación en el servidor. Esto, si bien es una ventaja para aquellos programadores de .NET que desean desarrollar aplicaciones web sin un conocimiento profundo de HTTP y HTML (se programa en términos de controles y eventos de .NET, de manera similar a las aplicaciones Windows), puede convertirse en un problema de gran complejidad cuando el desarrollador desea personalizar ciertos aspectos, como la inclusión de Widgets jQuery o la ejecución parcial del ciclo de vida de ASP.NET para producir solo un pedazo de la página. Piense en una página .aspx que represente un tablero de mando (Dashboard) donde cada elemento se representa como un control de usuario (ASP.NET User Control). Procesar dicha página podría suponer un costo operacional, por lo que sería útil poder dividir la misma y actualizar cada control de usuario de manera independiente. Usted pudiera pensar que el problema se resuelve incluyendo cada control de usuario en un Update Panel (ASP.NET AJAX Extensions), pero lamentablemente, producto de la génesis de ASP.NET, aunque viaje entre el servidor y el cliente solamente el código HTML de actualización de dicha región, se sigue ejecutando el ciclo de vida completo de la página ASP.NET; es por ello que se dice que es un proceso monolítico.

Una de las características más importantes que brinda ASP.NET MVC es la Separación de Responsabilidades o SoC (Separation of Concerns)4. Precisamente con la aplicación del patrón MVC, esta clásica página monolítica de ASP.NET se convierte en varios pedazos conceptualmente diferentes que pueden componerse en un resultado final según fuese necesario. Otra característica muy interesante en ASP.NET MVC es el control total que tiene el desarrollador sobre el código HTML generado; ya no hay controles "server-side" y no hay eventos, sino "vistas" que permiten la generación directa de código HTML [4].

Gracias a estas dos características, veremos que la inclusión e interacción de código jQuery con los recursos que se publican desde el servidor se hace mucho más fácil. Es por ello que decimos que con ASP.NET MVC hay más poder y más control, pero, ¿será factible el esfuerzo extra que se requiere?

El control "Selección de persona" con jQuery y ASP.NET MVC

Por simplicidad, en el ejemplo no se utilizará una conexión a base de datos, sino una colección de objetos en memoria. Supongamos un modelo que tendrá una representación de Tareas y Personas, como se muestra en el listado 1. Hay que resaltar que se utiliza la anotación con el atributo Required (System.ComponentModel.DataAnnotations) para validar el modelo, que en este caso significa que las propiedades Description y AssignedToID tienen que contener valor. La gracia es que ASP.NET MVC posteriormente convertirá dichas anotaciones en código Java­Script (utilizando el plug-in para validación de jQuery, que se encuentra en los ficheros jquery.validate.js y jquery.validate.unobtrusive.min.j), para que en el cliente (en el navegador) se haga la validación de la entrada de datos sin realizar un envío (POST) al servidor.

public class TaskModel
{
    public int Id { get; set; }
    
    [Required(ErrorMessage = "Descripción requerida")]
    public string Description { get; set; }

    [Required(ErrorMessage = "Debe seleccionar una persona válida")]
    public int AssignedToID { get; set; }
}

public class PersonModel
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Department { get; set; }
}
Listado 1. Modelo de datos

Se creará un controlador llamado TasksController.cs, que será el responsable de implementar las operaciones CRUD (Create, Retrieve, Update y Delete) para las Tareas. Adicionalmente, se crearán las respectivas vistas (Create.cshtml, Delete.cshtml, Details.cshtml, Edit.cshtml, Index.cshtml), utilizando en este caso el nuevo motor de vistas Razor [5] que viene con ASP.NET MVC 3.

Figura 1. Menú desplegable con autocompletado que facilita la selección de una persona

En la figura 1 se muestra la vista para la creación de una nueva tarea (producida por Create.cshtml) con el control de selección de personal funcionando. La idea consiste en permitirle al usuario ir escribiendo un nombre y mientras ir desplegando un menú de selección (claro, con una cantidad de elementos que quepan en pantalla) que autocomplete el prefijo escrito por el usuario. A pesar de que se brinde un menú desplegable para el autocompletado, como en toda interacción de este tipo, el usuario sigue teniendo la libertad para escribir lo que desee; es necesario validar el valor que se escriba y mostrar un error según haga falta (ver figura 2).

Figura 2. Validador que verifica que el texto sea un nombre válido en la lista de personas

El código para cada operación CRUD es bastante general. El listado 2 muestra cómo sería el código para crear una tarea; en él se puede apreciar que al hacer un pedido GET Tasks/Create simplemente se devuelve la vista Create.cshtml y se espera por que el usuario introduzca los datos. Cuando éste decide enviar los datos introducidos hacia el servidor, éstos se ligan automáticamente al modelo TaskModel utilizando las características predeterminadas de ASP.NET y simplemente se añaden al contexto de datos. La parte importante estaría entonces en cómo desarrollar la vista Create.cshtml.

public ActionResult Create()
{
  return View();
}
        
[HttpPost]
public ActionResult Create(
   TaskModel model)
{
  try
  {
    var context = new MyDatabase();
    context.Insert(model);
    context.SubmitChanges();
    return RedirectToAction("Index");
  }
  catch
  {
    return View(model);
  }
}
Listado 2. Acciones que implementan la creación de una nueva tarea

Una primera idea podría ser implementar desde cero el control deseado usando jQuery (para ver cómo usar jQuery desde Visual Studio, el lector puede referirse a un artículo anterior de DNM [6]). No obstante, antes de empezar de cero siempre es recomendable realizar una búsqueda y verificar si ya existe algún control para esto, o al menos alguno que tenga un comportamiento similar para utilizarlo como punto de partida. Para jQuery recomendamos visitar http://plugins.jquery.com. Con la plantilla de proyecto predeterminada que se incluye en Visual Studio 2010 al instalar la distribución ASP.NET MVC 3 [7] se brindan a su vez un conjunto de plug-ins jQuery listos para utilizar. Uno de estos es jQuery UI [8] (jquery-ui.js), que es una biblioteca que incluye barras de progreso, cajas de diálogos modales, animaciones y uno que usaremos en especial para este artículo: autocomplete.

<script type="text/javascript">
    $(document).ready(function () {
        $("#actriz").autocomplete({
            source: ["Angelina Jolie", "Penélope Cruz", "Salma Hayek"]
        });
    });
</script>

<input type="text" id="actriz" />
Listado 3. Uso básico del plug-in jQuery "autocomplete"

El plug-in jQuery autocomplete se vincula a una caja de texto y despliega un menú de selección que autocompleta la palabra que el usuario escribe, dada una fuente de datos que se especifica estáticamente o se carga dinámicamente vía AJAX. En el listado 3 puede verse su utilización básica: se crea una caja de texto con id actriz y se vincula el autocompletado (uso del selector de id utilizando la sintaxis #actriz), pasando una lista de actrices como fuente de datos. Para el desarrollo del control de selección de persona, será necesario que la lista de personas se cargue dinámicamente vía AJAX, por lo que primeramente hay que agregar una acción adicional al controlador de tareas (listado 4) que devuelva mediante un pedido GET y en formato JSON el listado de las primeras 10 personas cuyo nombre autocomplete cierto prefijo.

public ActionResult Persons(string name)
{
    var context = new MyDatabase();
    return new JsonResult
    {
        Data = context.GetRepository<PersonModel>()
            .Where(p => p.Name.StartsWith(name))
            .Take(10),
        JsonRequestBehavior = JsonRequestBehavior.AllowGet
    };
}
Listado 4. Acción que devuelve un array JSON con las personas (a lo sumo 10) cuyo nombre comienza con el valor del parámetro name

De manera predeterminada, en ASP.NET MVC no se permite devolver datos en formato JSON mediante el método GET por problemas de seguridad [9], aunque este comportamiento puede forzarse especificando JsonRequestBehavior.AllowGet. Lo interesante del uso de GET sobre POST es que puede utilizarse el navegador web para probar que la acción devuelva el resultado adecuado (figura 3). No obstante, es recomendable el uso de un depurador de HTTP como Fiddler [10], que permite visualizar e interactuar con el tráfico HTTP.

Figura 3. Utilizando la acción Persons desde el navegador web

Además de añadir código necesario para la funcionalidad del controlador de tareas, hay que programar el comportamiento de la vista, que constará de dos partes: el código HTML declarativo (que representará la forma visual) y el código JavaScript activo, que pone el comportamiento (en este caso, el plug-in autocomplete con jQuery).

En ASP.NET MVC, ligar los datos del formulario HTML y el objeto .NET que define el modelo (binding) se hace de manera predeterminada utilizando el atributo name de cada etiqueta HTML INPUT (controles HTML). Si el modelo (el tipo en .NET) tiene una propiedad con nombre Prop1 y existe un control HTML con atributo name cuyo valor sea Prop1, entonces automáticamente se le asigna el valor que se reciba en el FORM para Prop1 a la propiedad con el mismo nombre en .NET5. ASP.NET MVC interpreta los tipos básicos (int, float, bool, etc.) y hace la conversión automáticamente. Es por ello que si se quiere utilizar esta ligadura, se deben tener dos controles HTML, uno que almacene el nombre de usuario (autocompletado) y otro que almacene el Id, como se muestra en el listado 5.

<Button x:Name="btnVisualPlayer" Grid.Column="1" 
        RenderTransformOrigin="0.5,0.5"  
<div class="editor-label">
    @Html.LabelFor(model => model.AssignedTo)
</div>
<div class="editor-field">
    @Html.TextBox("AssignedTo")
    @Html.HiddenFor(model => model.AssignedToID)
    @Html.ValidationMessageFor(model => 
       model.AssignedToID)
</div>
Listado 5. Fragmento de código HTML que visualiza la caja de texto para la selección de persona, así como la etiqueta y el validador necesarios

Lo que queremos en este ejemplo es mostrar una caja de texto donde el usuario pueda escribir (con autocompletado) el nombre de una persona, pero el valor de interés que se enviará al servidor no será el nombre de usuario, sino el Id, que se almacena en un campo escondido (hidden) y está ligado automáticamente a la propiedad AssignedToID del modelo TaskModel. Entonces, será imprescindible diseñar un mecanismo que permita rellenar automáticamente el Id adecuado cuando el usuario seleccione una persona válida.

<script type="text/javascript">
    $(document).ready(function () {
        $("#AssignedTo").change(function (e) {
            $('#AssignedToID').val("");
        });
        $("#AssignedTo").autocomplete({
            source: function (request, response) {
                $.ajax({
                    url: '@Url.Action("Persons")',
                    cache: true,
                    data: { name: request.term },
                    dataType: 'json',
                    type: 'get',
                    timeout: 10000,
                    success: function (data) {
                        var mappedData = $.map(data, function (item) {
                            return {
                                id: item.Id,
                                label: item.Name,
                                value: item.Name,
                                Department: item.Department
                            }
                        });
                        response(mappedData);
                    }
                })
            },
            select: function (event, ui) {
                $('#AssignedToID').val(ui.item.id);
            }
        }).data("autocomplete")._renderItem = function (ul, item) {
            return $("<li></li>")
				.data("item.autocomplete", item)
				.append("<a>" + item.label + "<br/><b>Department: </b>" +
                                item.Department + '<div class="border"><div></a>')
				.appendTo(ul);
        };
    });
</script>
Listado 6. Código jQuery que da vida al control de selección de una persona, que no es más que una personalización del plug-in autocomplete

En el listado 6 se muestra el código completo que se añade a la vista Create.cshtml para darle este comportamiento a la caja de texto. Este código puede dividirse en tres partes para su análisis:

  • La configuración del pedido AJAX para solicitar la lista de personas y crear la fuente de datos.
  • El formato personalizado de los elementos del menú desplegable.
  • La acción de selección (que actualiza el valor del control escondido cuyo atributo HTML name es igual a AssignedToID con el Id de la persona seleccionada)

En este ejemplo, al plug-in autocomplete se le configuran dos opciones source y select para definir, respectivamente, la fuente de datos (AJAX) y la acción a ejecutar cuando se seleccione un elemento. En source se debe especificar una función que reciba dos parámetros, request y response. El parámetro request describe el pedido (de manera especial, el valor introducido por el usuario se almacena en el miembro term del request), y como parámetro response el plug-in autocomplete pasa una función (un callback asíncrono) que se utiliza para rellenar la lista de elementos del menú desplegable asociado a la caja de texto una vez que se haya realizado cierto procesamiento (que en este caso será un pedido AJAX).

Para poblar la fuente de datos se utiliza la función jQuery $.ajax, que permite realizar un pedido AJAX. Como se muestra en el ejemplo, el pedido AJAX recibe un objeto de configuración JSON con miembros que definen el comportamiento de dicho pedido. En el miembro url se configura a qué dirección se realiza el pedido, que en este caso es hacia la acción Persons previamente definida en el controlador TasksController.cs, mediante la combinación de código C# y JavaScript gracias al motor Razor (url: '@Url.Action("Persons")'6). En data se configuran los parámetros del query string en el pedido AJAX (en este caso el parámetro name), y de manera adicional se configuran otros parámetros como cache, dataType, type y timeout, que se auto-describen por su nombre. En el miembro success se puede especificar una función que se ejecuta cuando se recibe el resultado del servidor. En este caso, se cambia la estructura del resultado (utilizando la función jQuery $.map) para hacerlo compatible con el plug-in autocomplete, que espera un array de elementos con miembros label y value (donde label especifica el valor que se muestra en el menú desplegable y value el valor que se envía a la caja de texto cuando se realiza la selección), y se devuelve mediante la función callback response anteriormente descrita.

Finalmente, utilizando la función .data de jQuery se redefine la función _renderItem del prototipo autocomplete (que es lo que en este ejemplo define el autocomplete) asociado a la instancia del autocompletado que se está creando. En jQuery las "extensiones" (plug-ins como autocomplete) asociadas a algún elemento HTML se almacenan en un espacio aparte, fuera del objeto DOM (expando object) que representa al elemento, y son accedidas mediante la función .data. De esta forma, se puede redefinir el comportamiento predeterminado de cualquier extensión.

Figura 4. Relación entre el código HTML y su visualización para el control de selección de persona

El menú desplegable que brinda el plug-in autocomplete se presenta como una lista HTML

    , por lo que el formato de cada elemento se codificará entre etiquetas
  • . Para este ejemplo, además del nombre de usuario se mostrará el departamento al que pertenece, seguido por un separador personalizado definido mediante la clase CSS .border (figura 4).

    Cuando se selecciona un elemento del menú desplegable, se ejecuta la función definida en el miembro success, que en este caso actualiza el valor del campo AssignedToID mediante el código jQuery $('#AssignedToID').val(ui.item.id). Note que a la caja de texto se le vincula adicionalmente una acción cuando se cambia el texto ($("#AssignedTo").change(...)), que en este caso borra el valor del campo AssignedToID. Como el evento de selección del autocomplete se ejecuta después de lanzar cualquier evento onChange de la caja de texto, esta adición permitirá que cualquier edición que realice el usuario elimine el valor de AssignedToID, para que así este campo reciba valor una vez que se seleccione una persona válida. En caso de que no se rellene correctamente el Id (producto de no realizar alguna selección), se disparará el validador Required de ASP.NET MVC para el campo AssignedToID, obteniéndose finalmente el comportamiento propuesto para el control de selección de persona.

    Organizando el código jQuery

    Aunque el código anterior funciona, no es muy reusable que digamos, ya que está fuertemente acoplado a los nombres de los identificadores fijos (en este caso #AssignedTo y #AssignedToID), por lo que la reutilización en la vista Edit.cshtml tendría que hacerse copiando el código, acción que viola el principio DRY (Don't Repeat Yourself). Aunque JavaScript puede considerarse como un lenguaje orientado a objetos, su concepción es diferente a la de lenguajes como C#. En principio, en JavaScript no existen clases sino prototipos, y no existen constructores de objetos como miembros de clases, sino funciones que construyen objetos, las cuales pueden utilizarse de diferentes formas. Por ello, para lograr una unicidad en la creación, cada biblioteca JavaScript brinda una manera de reusar código. La biblioteca jQuery UI brinda un patrón conocido como jQuery UI Widget Factory [11] mediante el cual se puede extender el comportamiento de un widget. No obstante, como en este ejemplo lo que se quiere reusar son los parámetros de inicialización del plug-in autocomplete, es mucho más sencillo crear un nuevo método jQuery que se llame, por ejemplo, peoplePicker y que permita una utilización como la que se muestra en el listado 7.

    <script src="@Url.Content("~/Scripts/ui.PeoplePicker.js")" 
            type="text/javascript"></script>
    <script type="text/javascript">
        $(document).ready(function () {
            $("#AssignedTo").peoplePicker({
                DisplayControl: "#AssignedTo",
                ValueControl: "#AssignedToID",
                url: '@Url.Action("Persons")'
            });
        });
    </script>
    Listado 7. Encapsulando la inicialización del widget autocomplete en el método peoplePicker para simplificar la reusabilidad

    Para lograr esto, simplemente se crea un nuevo fichero de script (ui.PeoplePicker.js) con el esquema que se muestra en el listado 8 y se mueve al cuerpo del método peoplePicker el código del listado 7 reemplazando los literales "#AssignedTo" y "#AssignedToID" por options.DisplayControl y options.ValueControl, respectivamente; y en el pedido AJAX como url se utilizaría options.url.

    $.fn.extend({
        peoplePicker: function (options) {
            // Código de inicialización
        }
    });
    Listado 8. Caparazón que adiciona la función peoplePicker a jQuery

    De esta forma, se tendría el código del comportamiento en un fichero de script único que puede ser reutilizado desde cualquier vista en la que se requiera el control selección de una persona.

    Conclusiones

    Producto de la arquitectura cliente-servidor y desconectada de la Web, para desarrollar una aplicación web que cumpla con los requerimientos de la llamada Web 2.0 es necesario que el desarrollador tenga claro qué parte deberá desarrollarse en el cliente (HTML+JavaScript) y qué parte en el servidor. En esto hay que tener en cuenta que los extremismos son malos. Una aplicación que desarrolle la lógica totalmente en el servidor tiene la desventaja de que necesita más pedidos HTTP, lo cual es un inconveniente para los usuarios finales. Por otro lado, una aplicación que ejecute totalmente en el cliente tampoco es buena, al menos con el desarrollo tecnológico actual, ya que no aprovecharía características como SEO (Search Engine Optimization) que permiten posicionar la web en los principales buscadores de Internet y acceder directamente a la información concreta que publique la aplicación. La clave entonces sería que una aplicación pueda balancear adecuadamente lo que se hace en el lado del cliente y lo que se hace en el servidor. Y aquí es donde ASP.NET MVC resulta una plataforma ventajosa, ya que nos brinda más poder y más control en comparación con ASP.NET clásico, permitiéndonos inclinar la balanza a la posición deseada, al costo de tener en cuenta un nivel de detalle que dependerá de nuestro esfuerzo y de nuestro conocimiento de HTML y HTTP.

    Cierto que este principio, conocido como Do It Yourself (hágalo usted mismo), puede no ser ideal en todas las situaciones. Es por ello que a la par de ASP .NET MVC también se tiene una versión 4.0 de ASP .NET que incluye muchas mejoras con respecto a la versión anterior. Es de esperar que ASP.NET MVC, que es una tecnología relativamente joven (debutó en 2009), evolucione de tal manera que llegue a ser la herramienta por excelencia para el desarrollo de aplicaciones web con tecnología .NET. Pero por ahora, como se ha visto en este ejemplo sencillo, requiere de cierto esfuerzo adicional, y de conocer más detalles de HTML, HTTP y JavaScript.

    Por tanto, la respuesta a la pregunta de ¿más esfuerzo? incluida en el título de este artículo dependerá de vuestro equipo de desarrollo. Es verdad que implica más trabajo, pero este gasto adicional debe tributarle un beneficio mayor en cuanto a los requerimientos de su aplicación. Por lo pronto, no está mal comenzar a intentarlo (el código del proyecto puede descargarse del sitio web de dotNetManía), mientras esperamos que en el futuro la tecnología haga transparentes muchos de estos detalles que ahora pueden agobiar.

    blog comments powered by Disqus