Single Page Applications – Parte 2 – Server Technologies (ASP.NET MVC y ASP.NET Web API)

Durante estos posts nos enfocaremos principalmente en el Lado del Cliente (HTML y Javascript) pero de todas maneras necesitamos un Lado del Servidor para trabajar.

Este post lo dedicaremos a implementar el lado del servidor, para esto utilizaremos ASP.NET MVC 4 y ASP.NET Web API, aunque podría ser cualquier otra tecnología de nuestro agrado.

Nota: ASP.NET MVC 4 y ASP.NET Web API actualmente se encuentran en Beta y pueden ser descargados de aquí.

Al aplicar estas tecnologías, el lado del servidor quedaría de la siguiente manera:

dataservices

ASP.NET MVC 4

Utilizaremos esta tecnología para organizar y crear los recursos HTML, Javascript y CSS que luego serán servidos por parte del servidor.

Lo primero que haremos será crear un proyecto ASP.NET MVC 4 en blanco.

newproject

selecttemplate

solutionexplorer

Agregamos la hoja de estilos y modificamos el _Layout.cshtml según el Bakery Template de WebMatrix.

<!DOCTYPE html>
<
html
>
<
head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>Fourth Coffee -@ViewBag.Title</title>
    <link href="@System.Web.Optimization.BundleTable.Bundles.ResolveBundleUrl("~/Content/css")" rel="stylesheet" type="text/css" />
    <link href="@System.Web.Optimization.BundleTable.Bundles.ResolveBundleUrl("~/Content/themes/base/css")" rel="stylesheet" type="text/css" />
    <script src="@System.Web.Optimization.BundleTable.Bundles.ResolveBundleUrl("~/Scripts/js")"></script
>
</
head
>
<
body>
    <div id="page">
        <div id="header">
            <p class="site-title">
                <a href="~/">Fourth Coffee</a></p>
            <ul id="menu">
                <li><a href="~/">Home</a></li>
                <li><a href="~/About">About Us</a></li>
            </ul>
        </div>
        <div id="body">
            @RenderBody()
       
</div>
        <div id="footer">
            &copy;@DateTime
.Now.Year - Fourth Coffee
       
</div>
    </div
>
</
body
>
</
html
>

Creamos la clase HomeController que retorne la única vista de nuestra aplicación.

public class HomeController : Controller
{
   
public ActionResult
Index()
    {
           
return View();
    }
}

ASP.NET Web API

Nos permite crear y exponer de manera muy simple servicios bajo el protocolo HTTP siguiendo un estilo de arquitectura REST. Algunos de sus beneficios son:
  • Crear servicios REST.
  • Consultas ODATA.
  • Diferentes tipos de formatos: JSON, XML, etc.
  • Soporte de varios elementos ya existentes dentro ASP.NET: Model Binding, Routing, Validation, Filters, etc.
  • Soporte de inyección de dependencias.
  • Fácilmente testeable de manera unitaria.

Mediante ASP.NET Web API implementaremos nuestro Data Services endpoint para exponer servicios que envíen y reciban datos en formato JSON desde el cliente. Para esto realizaremos lo siguiente:

Creamos las clases del modelo para la aplicación y crearemos un DbContext típico de Entity Framework.

public class AppDbContext : DbContext
{
   
public AppDbContext() : base("bakery"
) { }
    public IDbSet<Product> Products { get; set; }
}

Utilizando Nuget agregamos la referencia a AspNetWebApi.Data, luego creamos un controller que herede de AspNetWebApi.Data.DbDataController, los controllers dentro de Web API funcionan de manera muy similar a MVC y permiten exponer servicios HTTP.

También creamos un servicio que devuelva todos los productos en formato JSON.

public class ProductsController : DbDataController<AppDbContext>
{
   
public IQueryable<Product
> Get()
    {
       
return DbContext.Products.AsQueryable();
    }
}

Nota: Retornar los datos como IQueryable y no como IEnumerable permite que se puedan realizar consultas ODATA.

Probamos nuestro servicio levantando toda la aplicación y accediendo a la siguiente URL: http://[domain]/api/products.

image

Esta URL corresponde a nuestro servicio HTTP, debido a que existe la siguiente ruta registrada dentro del Global.asax.

webapiroute

Asimismo, nuestro servicio HTTP dentro del controller tiene el nombre de “Get”, entonces por convención será mapeado automáticamente a todos los GET Request.

Por otro lado el servicio HTTP ha retornado XML, esto se debe a que Chrome envía por defecto en la cabecera de la solicitud Accept: application/xml; si en la cabecera se envía Accept: application/json, el servicio retornaría automáticamente los datos en formato JSON.

Por el momento esto es todo lo que necesitamos implementar en el lado del servidor.

Recursos Adicionales

Hasta el siguiente post.
Saludos.

Single Page Applications – Parte 1 – Definición y Arquitectura

En esta serie de posts veremos los conceptos detrás de las SPAs y como implementar una de estas aplicaciones.

¿Qué son las Single Page Applications?

SPAs son aplicaciones o sitios web que se ejecutan persistentemente sobre la misma página sin necesidad de recargar sin importar como el usuario interactúe.

Son aplicaciones ricas y responsivas que combinan lo mejor de la web y el escritorio, implementadas con Javascript y HTML5.

¿Qué beneficios tienen?

Por qué deberíamos considerar este tipo de aplicaciones:

  • Buena experiencia de usuario.
  • Reducen la carga en el servidor.
  • Similares a las aplicaciones nativas.
  • Pueden trabajar offline.
  • Pueden desplegarse en App-Stores

¿Cuál es su arquitectura?

Una aplicación “multi-page” tradicional usualmente sigue una arquitectura similar a la siguiente:

image 

  • Web UI: Contiene la lógica de negocio y a su vez se encarga de servir recursos HTML, JS, CSS desde el servidor.
  • Visible UI: Renderiza en el navegador la página web.
  • Application Layer: Código Javascript para manipular el comportamiento de la página dentro del navegador.

En cambio una aplicación SPA tiene una arquitectura similar a la siguiente:

image

  • Web UI: Contiene la lógica de negocio y a su vez se encarga de servir recursos HTML, JS, CSS desde el servidor.
  • Data Services: Provee datos en formato JSON o XML desde el servidor.
  • Data Access Layer: Se encarga de solicitar datos al servidor para que estos sean mostrados, enviar datos para que sean procesados.
  • Application Layer: También contiene mucha otra lógica relacionada a Navegación, Binding, etc.
  • Visible UI: También hace uso de templates para renderizar los datos.

¿Cómo implementar una SPA?

En los posts sucesivos veremos detalladamente como implementar una de estas aplicaciones. Como ejemplo utilizaremos el “Bakery Template” que es uno de los templates para construir aplicaciones web que viene por defecto dentro de WebMatrix.

Reutilizaremos su layout y estilos pero reharemos por completo toda la funcionalidad para que funcione como un SPA.

Hasta el siguiente post.
Saludos.

Refactoring Golf

Los días 11, 12 y 13 de Octubre se realizó en Buenos Aires (Argentina) la conferencia Agiles2011, es el evento latinoamericano más grande sobre metodologías ágiles, al cual tuve el placer de asistir y dar una ponencia junto con Gustavo Quiroz.

La ponencia se titula “Refactoring Golf”, es un workshop avanzado de Refactoring que está inspirado en la ponencia original realizada en el Software Craftmaship 2010.

Materiales

Les dejo todos los materiales del workshop, están decentemente reutilizables. Pueden utilizarlos libremente, solo déjenme sus comentarios e ideas para poder mejorar el material. Pueden hacer un fork al repositorio para agregarle más cosas pero no se olviden de avisarme para que yo también pueda usarlas =).

Descripción y Reglas.

Repositorio Github: Contiene código fuente, diapositivas y documentos para imprimir.

Ponencia Original.

Diapositivas

Adicionalmente he subido la presentación a Slideshare para que puedan comentarla y compartirla.

Retrospectiva

Esta es una imagen con la retrospectiva realizada al final del workshop.

retrospectiva

Saludos
Angel Núñez Salazar

Refactoring to Design Patterns

En este screencast vamos a ver cómo podemos refactorizar una clase para implementar el patrón State. El código pertenece al caso “Gumball Machine” que se encuentra en el libro Head First Design Patterns.

El código inicial y final que veremos en el screencast lo podemos descargar de https://github.com/snahider/Head-First-Design-Patterns.


Te recomiendo verlo en HD.
Un agradecimiento especial a Israel Antezana (@israelantezana) que me dio esta idea y con el cuál tuve una pequeña sesión de pair programming durante Agiles2011.

Update
Resharper 7 ya viene con el refactoring Extract Class que nos permite mover miembros de una clase a otra que aún no existe.

Saludos
Angel Núñez Salazar

Entity Framework 4.1 y ASP.NET MVC 3

Con la salida de EF 4.1 ha vuelto la discusión sobre el manejo de la Unit of Work en una aplicación web. En este caso vamos a ver algunos pasos utilizando EF 4.1, MVC 3 y StructureMap.

Paso 1: El Dominio

    public abstract class Entity
{
public int Id { get; set; }
}
    public class Post:Entity
{
public String Title { get; set
; }

public String Body { get; set
; }

public List<Comment> Comments { get; set; }

}
    public class Comment:Entity
{
public string Content { get; set
; }

public int PostId { get; set
; }

public Post Post { get; set; }
}

Paso 2: DbContext y la cadena de conexión

Creamos una clase que extienda de DbContext para que a través de ella interactuemos con la base de datos. Por defecto busca una cadena de conexión que tenga el mismo nombre de la clase que lo implementa, en este caso una cadena con el nombre de DatabaseContext. Podemos cambiar el nombre de esta cadena si ingresamos un nuevo parámetro al constructor del DbContext.

    public class DatabaseContext : DbContext
{

public
DatabaseContext() : base("Blog") {}


}
    <add name="Blog" connectionString="xXx" providerName="System.Data.SqlClient" />

Paso 3: Los Repositorios

No me gusta la idea de tener todos los DbSet dentro del DatabaseContext, es por esto que creamos un repositorio base que instancie un DbSet para su correspondiente entidad y también agregamos algunos métodos comunes.

    public class Repository<T> where T : Entity
{
public
Repository()
{
var database = ObjectFactory.GetInstance<DatabaseContext
>();
this
.Set = database.Set<T>();
}

protected readonly DbSet
<T> Set;

public IEnumerable
<T> All()
{
return this
.Set.ToList();
}

public void
Add(T entity)
{
this
.Set.Add(entity);
}

public T Get(int
id)
{
return this
.Set.SingleOrDefault(x=>x.Id==id);
}

public void
Delete(T entity)
{
this.Set.Remove(entity);
}
}

En este caso no estamos inyectando el DatabaseContext por el constructor sino lo obtenemos a través de un Service Locator, esto con la finalidad de no tener que escribir el constructor en todos los repositorios hijos.

    public interface IPostRepository
{
Post Get(int
id);

void Add(Post
post);

void Delete(Post
post);

IEnumerable<Post
> All();

IEnumerable<Comment> Comments(int
idPost);
}

public class PostRepository : Repository<Post>, IPostRepository
{
public IEnumerable<Comment> Comments(int
idPost)
{
return this.Set.SelectMany(p => p.Comments)
.Where(c => c.Post.Id == idPost).ToList();
}
}

Paso 4: Inyección de Dependencias

En ASP.NET MVC 3 se ha formalizado la inyección de dependencias, es por esto que podemos implementar la nueva interfaz IDependencyResolver e indicarle a MVC que utilice esta clase para determinar cuál es la instancia correcta cuando se solicite un determinado tipo.

    public class StructureMapDependencyResolver:IDependencyResolver
{
public StructureMapDependencyResolver(IContainer
container)
{
this
.container = container;
}

private readonly IContainer
container;

public object GetService(Type
serviceType)
{
if
(serviceType.IsAbstract || serviceType.IsInterface)
{
return this
.container.TryGetInstance(serviceType);

}

return this
.container.GetInstance(serviceType);

}

public IEnumerable<object> GetServices(Type
serviceType)
{
return container.GetAllInstances<object>()
.Where(s => s.GetType() == serviceType);
}
}

Ahora configuramos StructureMap y registramos la clase anterior dentro del Global.asax. El punto más importante en cuanto al manejo de la unidad de trabajo es configurar el contenedor para que se tenga una única instancia del DatabaseContext a nivel de todo un request.

    protected void Application_Start()
{
ObjectFactory
.Initialize(x =>
{
x.For<
DatabaseContext
>().HybridHttpOrThreadLocalScoped();
x.For<
IPostRepository>().Use<PostRepository
>();
});

DependencyResolver
.SetResolver(
new StructureMapDependencyResolver(ObjectFactory
.Container));

AreaRegistration
.RegisterAllAreas();
RegisterGlobalFilters(
GlobalFilters
.Filters);
RegisterRoutes(
RouteTable.Routes);
}

Si utilizamos StructureMap necesitamos limpiar todos los objetos que tienen un ciclo de vida a nivel de request, en este caso el DatabaseContext.

    protected void Application_EndRequest(object sender, EventArgs e)
{
ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
}

Paso 4: El Filtro de Transacción

Ahora que se comparte el mismo DatabaseContext en todo un request, cuando este finalice, necesitamos enviar a la base de datos todos los cambios que se hayan realizado como si fueran una sola unidad o transacción. Para esto creamos un filtro de acción que guarde los cambios realizados dentro del DatabaseContext.

    public class TransactionAttribute : ActionFilterAttribute
{
public DatabaseContext Context { get; set
; }

public override void OnActionExecuted(ActionExecutedContext
filterContext)
{
if (filterContext.Exception == null
)
{
try
{
Context.SaveChanges();
}
catch (DbUpdateConcurrencyException
)
{
//manejar errores de concurrencia como sea conveniente
}
catch (Exception
)
{
//manejar errores genéricos como sea conveniente
}
}
}
}

Para poder inyectar la instancia correcta del DatabaseContext dentro del filtro, necesitamos extender la clase FilterAttributeFilterProvider.

    public class StructureMapFilterProvider : FilterAttributeFilterProvider
{
private readonly IContainer
container;
public StructureMapFilterProvider(IContainer
container)
{
this
.container = container;
}

public override IEnumerable<Filter
> GetFilters
(
ControllerContext controllerContext, ActionDescriptor
actionDescriptor)
{
var filters = base
.GetFilters(controllerContext, actionDescriptor);

foreach (var filter in
filters)
{
container.BuildUp(filter.Instance);
}

return filters;
}
}

Modificamos el Global.asax para registrar la clase anterior e indicarle a StructureMap que realice inyección a través de propiedades para el DatabaseContext.

    protected void Application_Start()
{
ObjectFactory
.Initialize(x =>
{
x.For<
DatabaseContext
>().HybridHttpOrThreadLocalScoped();
x.For<
IPostRepository>().Use<PostRepository
>();
x.SetAllProperties(p => p.OfType<
DatabaseContext
>());
});

DependencyResolver
.SetResolver(
new StructureMapDependencyResolver(ObjectFactory
.Container));
FilterProviders
.Providers.Add(
new StructureMapFilterProvider(ObjectFactory
.Container));

AreaRegistration
.RegisterAllAreas();
RegisterGlobalFilters(
GlobalFilters
.Filters);
RegisterRoutes(
RouteTable.Routes);
}

Paso 5: Registrando las entidades en el DatabaseContext

Debido a que los DbSets de nuestras entidades no se encuentran dentro del DatabaseContext, este no las reconoce para poder utilizarlas; para registrarlas debemos sobrescribir el método OnModelCreating y por cada entidad llamar al método genérico modelbuilder.Entity<T>.

    public class DatabaseContext : DbContext
{
public DatabaseContext() : base("Blog"
){}

protected override void OnModelCreating(DbModelBuilder
modelBuilder)
{
var method = typeof(DbModelBuilder).GetMethod("Entity"
);

var types = Assembly
.GetExecutingAssembly().GetExportedTypes()
.Where(t =>
typeof(Entity
).IsAssignableFrom(t) &&
!t.IsAbstract && !t.IsInterface)
.ToList();

foreach (var type in
types)
{
var genericMethod = method.MakeGenericMethod(new
[] { type });
genericMethod.Invoke(modelBuilder,
null);
}
}
}

Paso 6: Utilizando lo anterior en los Controllers

Creamos un controller y algunas acciones que utilicen los repositorios y el filtro anteriormente creado.

    public class PostsController : Controller
{
private readonly IPostRepository
postRepository;

public PostsController(IPostRepository
postRepository)
{
this
.postRepository = postRepository;
}

public ActionResult
Index()
{
IEnumerable<Post
> posts = postRepository.All();
return
View(posts);

}

public ActionResult
Create()
{
return
View();
}

[
HttpPost
]
[
Transaction
]
public ActionResult Create(Post
post)
{
postRepository.Add(post);
return RedirectToAction("index"
);
}

}

Último Paso: Creando la Base de datos

Por último podemos crear la base de datos y sus tablas de forma manual o dejar que estas se creen automáticamente cuando se instancie por primera vez el DatabaseContext.

Con esto ya tenemos todo lo necesario para el manejo de la unidad de trabajo a nivel de request.
Hasta el siguiente post.

Saludos
Angel Núñez Salazar

Integración Continua – Resultado Final

En este post veremos cuál es el resultado final al que llegaríamos si realizamos toda la configuración de nuestro proceso de integración continua.


Te recomiendo verlo activando la calidad HD

En la serie de posts sobre integración continua hablaremos con más detalle de todo lo visto en el video. No se lo pierdan.

Saludos.
Angel Núñez Salazar

Integración Continua Series

Acá tenemos la lista de posts de la serie de Integración Continua.

Saludos
Angel Núñez Salazar