[HowTo] VS Code | Crear una WebApi con Asp.Net Core en Linux

Como comente en post anterior Microsoft está entrando fuertemente al mundo OpenSource. Por esta razon hoy podemos disfrutar de Asp.Net Core en la plataforma Linux, en mi caso KUbuntu, junto a algunas herramientas muy poderosas com VS Code.

Objetivo

El objetivo de esta publicación tiene un tutorial de como debemos preparar nuestro linux para hacer uso de Asp.Net junto a una aplicacion del tipo WebApi que usaremos en post futuros.

Audiencia

Este documento está dirigido a personas que conocen muy poco o nada sobre el tema o personas que desarrollan tareas de consultoría de desarrollo o que simplemente están interesados en leer e investigar sobre la tecnología alcanzada por esta publicación.

Desarrollo

Para empezar lo que haremos es instalar Net Core es nuestro linux. Lo haré sobre mi distribución preferida que es KUbuntu. Primero necesitamos instalar las llaves de producto y actualizar el listado de paquetes en nuestra distribución linux. Abriremos una consola y ejecutamos los siguiente comandos estos 4 comandos en este orden:

curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg
sudo mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg
sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-zesty-prod zesty main" > /etc/apt/sources.list.d/dotnetdev.list'
sudo apt-get update

 

Ya ejecutado todo estamos listos para instalar:

sudo apt-get install dotnet-sdk-2.0.0

Una vez que instalamos todo y configurado estamos listso. Vamos a crear nuestra primera aplicación en linux. Crearemos una carpeta y ejecutaremos el comando dotnet que nos ayuda a crear la plantilla:

mkdir MvcMyApp
cd MvcMyApp
dotnet new mvc

Ahora podemos abrir desde VS Code que instalamos en un post anterior. Con Solo escribir “code .” se abrirar el VS Code en la carpeta donde estamos parados. Ahora necesitaremos instalar VS Code C# extensión. Lo haremos desde el icono de VS Code, lo buscaremos y lo instalaremos.

Luego de instalarlo recargamos la ventana y comenzará a instalarse algunos componentes necesarios que nos permitirá usar la depuración en VS Code.

Ahora solo nos queda presionar F5 y guala!

 

Una WebApi para ejemplos

Lo que haremos ahora es crear una WebApi Rest que la usaremo en futuros ejemplos en un el tutorial de TypeScript. Vamos a necesitar tener instalado:

  • DB Browser for Sqlite (podemos instalarlos desde el gestor de software que viene con KUbuntu.
  • Postman (podemos descargarlo desde aqui https://www.getpostman.com/)

Empezaremos con crear una carpeta DemoApi y dentro ejecutaremos los comandos de creación y lo abriremos, solo hay que seguir los siguientes comando en orden:

mkdir DemoService
cd DemoService
dotnet new webapi
code .

Nuevamente nos aparecerán los warning a los que debemos darle yes.

Crearemos la base de datos con Sql DB Browser dentro de una carpeta data. La base de Datos la llamaremos DBDemoServices y corremos el script que dejare en el github dentro de la carpeta data se llama base.txt. Este script creará 2 tablas e insertara datos para hacer el demo.

En nuestro servicio usaremos Entity Framework Core como herramienta de acceso a datos. No ayudará con nuestra base de datos Sqlite. Agregaremos las siguientes lineas en nuestro archivo DemoService.csproj:

<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.0"/>

Desde la consola ejecutaremos dotnet restore para actualizar nuestros paquetes:

Comenzaremos creando nuestros modelos y nuestro DBContext. Crearemos 3 archivos, Customer.cs, CustomerType y DemoServiceContext.cs. Todos dentro de una carpeta Models con la siguiente clases dentro:

Customer.cs

using System;
using System.Collections.Generic;

namespace DemoService.Models
{
public partial class Customers
{
public int CustomerId { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string SurName { get; set; }
public string EmailAddress { get; set; }
public int? CustomerTypeId { get; set; }
public string Notes { get; set; }

public virtual CustomersTypes CustomerType { get; set; }
}
}

CustomerType.cs

using System;
using System.Collections.Generic;

namespace DemoService.Models
{
public partial class CustomersTypes
{

public CustomersTypes()
{
Customers = new HashSet<Customers>();
}

public int CustomerTypeId { get; set; }
public string Description { get; set; }

public virtual ICollection<Customers> Customers { get; set; }

}

}

DemoServiceContext.Typecs

using Microsoft.EntityFrameworkCore;
using DemoService.Models;

namespace DemoService.Models
{
public class DemoDBContext : DbContext
{
public DemoDBContext(DbContextOptions<DemoDBContext> options)
: base(options)
{
}
public virtual DbSet<Customers> Customers { get; set; }
public virtual DbSet<CustomersTypes> CustomersTypes{ get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Filename=BDemoSevice.db;");
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Customers>(entity =>
{
entity.HasKey(e => e.CustomerId)
.HasName("PK_Customers");

entity.Property(e => e.EmailAddress).HasColumnType("varchar(150)");

entity.Property(e => e.FirstName).HasColumnType("varchar(50)");

entity.Property(e => e.MiddleName).HasColumnType("varchar(50)");

entity.Property(e => e.Notes).HasColumnType("varchar(5000)");

entity.Property(e => e.SurName).HasColumnType("varchar(50)");

entity.HasOne(d => d.CustomerType)
.WithMany(p => p.Customers)
.HasForeignKey(d => d.CustomerTypeId)
.HasConstraintName("FK_Customers_CustomersTypes");
});

modelBuilder.Entity<CustomersTypes>(entity =>
{
entity.HasKey(e => e.CustomerTypeId)
.HasName("PK_CustomersTypes");

entity.Property(e => e.CustomerTypeId).ValueGeneratedNever();

entity.Property(e => e.Description).HasColumnType("varchar(50)");
});
}

}
}

Debemos agregar en nuestro archivo Startup.cs la inyección de nuestra base de datos.

public void ConfigureServices(IServiceCollection services){
services.AddMvc();
services.AddDbContext<DemoDBContext>();
}

Ahora que tenemos todo configurado crearemos dentro de la carpeta Controllers un archivo llamado customers.cs donde tendremos nuestra api:

CustomersControllers.cs

using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using DemoService.Models;
using System.Linq;

namespace DemoService.Controllers
{
[Route("api/[controller]")]
public class CustomersController : Controller
{
private readonly DemoDBContext _context;

public CustomersController(DemoDBContext context)
{
_context = context;
}

[HttpGet]
public IEnumerable<Customers> GetAll()
{
return _context.Customers.ToList();
}

}
}

Para comprobar que funciona correctamente ejecutaremos la aplicación presionando F5 y lo invocamos con el Postman:

Bien está funcionando correctamente. Agregamos los métodos de crear, editar y eliminar y también agregaremos el CustomersTypesController.cs y un modelo que será ModeCustomerPage.cs que será para devolver páginas de Customers.

Models/ViewModelPage.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using DemoService.Models;

namespace DemoService.Models
{
public class ViewModelPage
{
public int CountPages { get; set; }
public int CountCustomers { get; set; }
public int ActualPage { get; set; }
public IList<Customers> customers { get; set; }
}
}

CustomersControllers.cs

using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using DemoService.Models;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;

namespace DemoService.Controllers
{
[Route("api/[controller]")]
public class CustomersController : Controller
{
private readonly DemoDBContext _context;

public CustomersController(DemoDBContext context)
{
_context = context;
}

[HttpGet]
public IEnumerable<Customers> GetCustomers()
{
return _context.Customers;
}

[HttpGet("{id}")]
public async Task<IActionResult> GetCustomers([FromRoute] int id)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}

var customers = await _context.Customers.SingleOrDefaultAsync(m => m.CustomerId == id);

if (customers == null)
{
return NotFound();
}

return Ok(customers);
}

[HttpGet]
[Route("{numberPage}/{perPage}")]
public ViewModelPage GetCustomerPaging(int numberPage, int perPage)
{

int _customersCount = GetCustomers().Count();
int _countPages = (_customersCount / perPage) + ( _customersCount % perPage == 0 ? 0 : 1);

var page = new ViewModelPage();
page.ActualPage = numberPage;
page.CountCustomers = GetCustomers().Count();
page.CountPages = _countPages;
page.customers = GetCustomers().Skip(numberPage * perPage).Take(perPage).ToList();

return page;
}

[HttpPut("{id}")]
public async Task<IActionResult> PutCustomers([FromRoute] int id, [FromBody] Customers customers)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}

if (id != customers.CustomerId)
{
return BadRequest();
}

_context.Entry(customers).State = EntityState.Modified;

try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!CustomersExists(id))
{
return NotFound();
}
else
{
throw;
}
}

return NoContent();
}

[HttpPost]
public  IActionResult PostCustomers([FromBody] Customers customers)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}

_context.Customers.Add(customers);
_context.SaveChanges();

return CreatedAtAction("GetCustomers", new { id = customers.CustomerId }, customers);
}

[HttpDelete("{id}")]
public async Task<IActionResult> DeleteCustomers([FromRoute] int id)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}

var customers = await _context.Customers.SingleOrDefaultAsync(m => m.CustomerId == id);
if (customers == null)
{
return NotFound();
}

_context.Customers.Remove(customers);
await _context.SaveChangesAsync();

return Ok(customers);
}

private bool CustomersExists(int id)
{
return _context.Customers.Any(e => e.CustomerId == id);
}

}
}

CustomersTypesControllers.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using DemoService.Models;

namespace DemoService.Controllers
{

[Route("api/[controller]")]
public class CustomersTypesController : Controller
{
private readonly DemoDBContext _context;

public CustomersTypesController(DemoDBContext context)
{
_context = context;
}

[HttpGet]
public IEnumerable<CustomersTypes> GetCustomersTypes()
{
return _context.CustomersTypes;
}

[HttpGet("{id}")]
public async Task<IActionResult> GetCustomersTypes([FromRoute] int id)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}

var customersTypes = await _context.CustomersTypes.SingleOrDefaultAsync(m => m.CustomerTypeId == id);

if (customersTypes == null)
{
return NotFound();
}

return Ok(customersTypes);
}

[HttpPut("{id}")]
public async Task<IActionResult> PutCustomersTypes([FromRoute] int id, [FromBody] CustomersTypes customersTypes)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}

if (id != customersTypes.CustomerTypeId)
{
return BadRequest();
}

_context.Entry(customersTypes).State = EntityState.Modified;

try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!CustomersTypesExists(id))
{
return NotFound();
}
else
{
throw;
}
}

return NoContent();
}

[HttpPost]
public async Task<IActionResult> PostCustomersTypes([FromBody] CustomersTypes customersTypes)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}

_context.CustomersTypes.Add(customersTypes);
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateException)
{
if (CustomersTypesExists(customersTypes.CustomerTypeId))
{
return new StatusCodeResult(StatusCodes.Status409Conflict);
}
else
{
throw;
}
}

return CreatedAtAction("GetCustomersTypes", new { id = customersTypes.CustomerTypeId }, customersTypes);
}

[HttpDelete("{id}")]
public async Task<IActionResult> DeleteCustomersTypes([FromRoute] int id)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}

var customersTypes = await _context.CustomersTypes.SingleOrDefaultAsync(m => m.CustomerTypeId == id);
if (customersTypes == null)
{
return NotFound();
}

_context.CustomersTypes.Remove(customersTypes);
await _context.SaveChangesAsync();

return Ok(customersTypes);
}

private bool CustomersTypesExists(int id)
{
return _context.CustomersTypes.Any(e => e.CustomerTypeId == id);
}
}
}

Listo nuestra aplicación está completa ahora probaremos un post con el postman.

Conclusión

Hemos instalado y configurado .Net Core y Asp.Net Core en nuestro linux. También hicimos una pequeña base de datos y nos conectamos a ella por medio de WebApi para tener servicio Rest. Estos servicios rest los usaremos en otros post para algunos ejemplo. Pueden descargar el código complete de GitHub desde esta url https://github.com/withoutdebugger/DemoService

 

 

[HowTo] Visual Studio Code | En Linux

Microsoft en estos últimos tiempos está apostando al OpenSource. Una de las cosas que ha impulsado es el uso de sus herramientas en este caso el editor liviano pero extremadamente potente VS Code.

Objetivo

El objetivo de esta publicación tiene un tutorial de como podemos instalar Visual Studio Code en linux.

Audiencia

Este documento está dirigido a personas que conocen muy poco o nada sobre el tema o personas que desarrollan tareas de consultoría de desarrollo o que simplemente están interesados en leer e investigar sobre la tecnología alcanzada por esta publicación.

Desarrollo

Visual Studio Code es un editor de código ligero y muy potente el cual podemos ejecutarlo en cualquier plataforma como Windows, MacOs o Linux. Viene con soporte Intellisense y para varios lenguajes de forma nativa. También podemos utilizar controladores de código fuente como Git o TFS. Por último, la característica que más me gusta, es totalmente extensible. Gracias a esto última característica tenemos una gran cantidad de plugin que podemos instalar para cualquier lenguaje que utilicemos.

Yo he elegido, solo por gusto personal, la distribución Kubuntu. No por nada en particular solo me gusta mas KDE. Lo primero que debemos hacer es descargar el paquete desde la página con la extensión .deb. Pueden hacerlo desde este link  https://code.visualstudio.com/

Una vez que lo tenemos descargado debemos abrir una consola, ir a la carpeta donde descargamos los archivos. En KUbuntu tenemos la posibilidad de instalarlo por medio de QApt:

Y listo! tenemos nuestro VS Code instalado

Para los más audaces podemos instalarlo por medio de consola completamente. Comencemos:

curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg
sudo mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg
sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/vscode stable main" > /etc/apt/sources.list.d/vscode.list'

“Nota: puede ser que no tengamos curl instalado por esta razón primero deberemos instalarlo con el siguiente comando apt-get install curl“

Si no hemos tenido ningún problema, el siguiente paso es actualizar nuestro repositorio de paquetes para esto usaremos el siguiente comando

sudo apt-get update

Por último, lo que nos queda es instalar el VS Code desde el repositorio.

sudo apt-get install code

Ya lo tenemos instalado y listo para comenzar disfrutar de esta gran herramienta.

Conclusión

Hemos instalado VS Code en nuestro linux de una forma muy sencilla. En próximos post veremos como hacer una aplicación .Net.

[Article] Azure | Content Delivery Network (CDN)

Hoy quiero tocar el tema de CDN específicamente Azure Content Delivery Network. Esta herramienta nos ofrece una solución global para tener contenido disponible usando un ancho de banda de gran velocidad. En esta red podemos almacenar objetos en cache publicos tales como archivos, aplicaciones, maquinas virtuales o cualquier objeto que pueda almacenarse en vía http o https.

Objetivo

El objetivo de esta publicación es tocar las posibles utilizaciones y alcance del manejo CDN para mejorar la performance y la escalabilidad en nuestras aplicaciones Asp.Net Core.

Audiencia

Este documento está dirigido a desarrolloradores que quieren conocer Asp.Net core y el uso de Azure CDN o personas que desarrollan tareas de consultoría de desarrollo o que simplemente están interesados en leer e investigar sobre la tecnología alcanzada por esta publicación.

Desarrollo

Usualmente CDN lo utilizamos para entregar contenido estático, como imágenes, archivos javascript, archivos de estilos css, etc. Aunque en su gran mayoría se usa para esto, también, podemos utilizar para brindar contenido dinámico almacenando su parametrización y devolviendo el mismo resultado.

Una de las principales ventajas del uso es que posee una latencia muy pequeña lo cual hace que se pueda entregar contenido extremadamente rápido sin estar atado a una ubicación geográfica o bien brindando lo más cerca posible del usuario que la solicita.

 

Usar CDN no ayuda a reducir la carga de la aplicación ya que liberamos a los servidores de procesamiento innecesario al entregar el contenido. Esto aumenta el rendimiento y reduce el costo del consumo de red como también aumentando la disponibilidad.

Veamos algunos casos típicos donde podemos usar CDN:

  • Recursos Estáticos: Nuestros sitios web contienen una gran cantidad de contenidos estáticos como imágenes, hojas de estilo, documentos, archivos, páginas web completas o parciales o cualquier contenido que no necesita ser procesado por el servidor.
  • Contenido Estático para dispositivos móviles: Por medio de servicio web se puede proporcionar datos estáticos a este tipo de clientes.
  • Media y Video: Podemos transferir videos pequeños. El gran beneficio es que baja mucha la latencia y aumenta la disponibilidad de este tipo de contenido. Algo para tener en cuenta es que Azure Media Services puede ser integrado con Azure CDN para entregar contenido.
  • Aumento de Demanda: podemos evitar que el aumentar la demanda no aumente nuestros costos de consumo. Por ejemplo, si nuestra aplicación lanza una nueva versión, aumentará la demanda de nuestros archivos y contenido para las nuevas características.

Ahora bien, qué debemos tener en cuenta para el uso o implementación:

  • Debemos tenerlo en cuenta durante nuestro proceso de desarrollo e implementación de contenido estático y recursos de nuestras aplicaciones.
  • Debemos considerar que nuestro contenido será actualizado, por esto, debe estar versionado.
  • Es difícil realizar pruebas locales o probar aplicaciones locales.
  • El contenido público es enviado desde dominios diferentes lo cual puede afectar a los resultado SEO.
  • Podemos encontrarnos con entornos donde no podamos consumir recursos directamente desde el CDN. Para esto es necesario hacer una implementación especial.
  • Si la red es privada, como una empresa, tal vez no sea una buena opción por restricciones de seguridad de la misma red.}

Usar CDN es una buena práctica para minimizar la carga, maximizar la disponibilidad y rendimiento de nuestras aplicaciones. Es altamente recomendable para implementar en nuestras aplicaciones web. Veamos algunas estrategias:

Entrega

  • CDN solamente necesita un endpoint HTTP o HTTPS la cual será utilizada para acceder y para almacenar el contenido.
  • Debemos recordar que el Blob al cual esté direccionado debe ser público.
  • Si nuestro lo seteamos como root en una WebApp todo el sitio quedará disponible por CDN.
  • Cuando las fuentes son dinámicas, podemos utilizar parametros via QueryString como llaves para almacenar en caché el contenido de este tipo.
  • Debemos recordar que los recursos del tipo blobs son sensibles a mayusculas y minusculas.

Despliegue

  • El contenido puede necesitar ser desplegado inmediatamente. Debemos considerar que será necesario un mecanismo de control de versiones para poder entregar las el contenido correctamente.
  • La comprensión, unión y minificación de archivos son técnicas utilizadas para reducir el tiempo de carga de los clientes y son compatibles con CDN.
  • Al utilizar la compresión de contenido será enviado en un formato zip o gzip. Por default Azure comprimira el contenido si el procesador se encuentra menos de un 50% de consumo.

Enrutamiento y control de versiones

  • Si necesitamos mantener control de versiones podemos crear un nuevo CDN y conservar el antiguo para versiones anteriores de nuestra aplicación.
  • La implementación de nuevas versiones de contenido estático será necesario tener en cuenta si fueron almacenados en el caché del CDN.
  • Podemos restringir acceso por país. Azure CDN nos permite filtrar contenido por origen de la petición y restringir la entrega del contenido.

Control de Caché

  • Debemos considerar el almacenamiento del caché en el sistema. Podemos configurar propiedades de caché desde el CDN y para el cliente podemos configurar los encabezados HTTP.
  • Podemos purgar el caché eliminandolos manualmente, convertir el blob en privado. Debemos tener en cuenta que los archivos solo serán eliminados cuando el tiempo de vida expire realmente si no se especificado un tiempo de cache.
  • En las aplicaciones web podemos establecer el almacenamiento de cache y su caducidad mediante el elemento clientCache del namespace system.webServer / staticContent en el archivo web.config.
  • Si el contenido es dinámico no debemos olvidar setear la propiedad Cache.SetExpires en cada página.

Seguridad

  • En el CDN puede entregar contenido a través de HTTPS utilizando certificados proporcionado por el mismo. También estará disponible en HTTP ya que no es posible bloquear los elementos por este canal.
  • Podemos tener algunos inconvenientes al usar peticiones AJAX debido a que se hace uso del objeto XMLHttpRequest cuando son solicitados datos, imágenes o fuentes de un dominio diferente. La gran mayoría de los navegadores evitan el consumo de objetos cruzados (CORS) si el servidor no está configurado correctamente.

Dominios personalizados

  • Podemos usar dominios personalizados y utilizarlo para acceder a recursos a través de CDN.
  • Podemos utilizar subdominios utilizando registros CNAME en nuestros CDN. Recordemos que si usamos este método no podremos usar SSL ya que el CDN utiliza sus propios certificados.

Tips para SEO

  • Debemos incluir el encabezado REL en cada una de las paginas o los recursos.
  • Utilizar un subdominio CNAME para acceder a los recursos.}
  • Tener en cuenta que la dirección IP del CDN puede ser diferente del pais donde se encuentra su aplicación.
  • Mantener la misma estructura si estamos usando almacenamiento Blob Azure

Supervisión y registro

Debemos agregar en nuestra estrategia de construcción de aplicaciones el planeamiento para implementación de CDN para poder detectar y medir fallas o problemas de latencia. Desde el portal de Azure podemos encontrar un panel de administración.

Demos Considerar:

  • Ancho de banda
  • Datos transferidos
  • Hits
  • Estado del caché
  • Cache Hit ratio
  • Solicitudes desde IPV4 / 6

Costos

Algo para tener muy presente es que se cobra por transferencia de información saliente desde nuestro endpoint CDN. Debemos incluir el costo de almacenamiento de recursos que se alojan activamente y sus transacciones. Debemos balancear los tiempo de expiración de caché, no debe ser muy largos para mantener un caché actualizado como no deben ser muy cortos para aumentar los costos.

Agrupamiento y minificación

Una excelente práctica es la agrupación  y la minificación de recursos. Esta práctica reduce notoriamente los tiempos de descarga debido a su reducción en tamaño.

Tenemos varias formas de manejar este tipo de prácticas. Podemos hacerlo desde .Net MVC como también con algunas herramientas de NodeJS. Mi recomendación es ver WebPack, una herramienta muy popular dedica a este tipo de funciones.

Conclusión

CDN posee un gran soporte para la utilización de caché tanto en entrega de contenido estático o contenido dinámico. Podemos aprovechar esta funcionalidad para aumentar el rendimiento y la escalabilidad de nuestras aplicaciones. En futuros artículos veremos como usar Azure CDN.

[Article] Asp.Net Core | Trabajando con Cache Distribuido

Vamos a continuar con el tema de uso de cache pero esta vez lo veremos Distribuido. El caché distribuido puede ayudar no solamente en la performance las aplicaciones sino también en la escalabilidad de nuestras aplicaciones.

Objetivo

El objetivo de esta publicación es tocar las posibles utilizaciones y alcance del manejo de del caché distribuido para mejorar la performance y la escalabilidad en nuestras aplicaciones Asp.Net Core.

Audiencia

Este documento está dirigido a desarrolladores que quieren conocer Asp.Net Core y el uso de cache distribuido o personas que desarrollan tareas de Consultoría de desarrollo o que simplemente están interesados en leer e investigar sobre la tecnología alcanzada por esta publicación.

Desarrollo

¿Qué es el caché distribuido? Significa que podemos usar caché y múltiples servidores. En el caso de Microsoft Azure puede ser alrededor del mundo. El uso de cache distribuido tiene muchas ventajas como, por ejemplo:

  • Los datos almacenados en el caché siempre son coherentes entre los servidores. No depende de donde el usuario consulte los datos todos verán la misma información.
  • Si es necesario reiniciar un equipo el cache seguirá vivo como también si los servidores necesitan escalar su capacidad. Se pueden agregar o quitar servidores sin necesidad de afectar al cache.
  • Las bases de datos o repositorios de datos tendrán menos consultas, un muchos casos a veces hasta ninguna.

Como cualquier caché existente implementar caché distribuido puede aumentar significativamente la capacidad de respuesta de nuestras aplicaciones debido que los datos son recuperados más rápido que desde una base de datos. También pueden reducir el costo de consumo de nuestros repositorios de bases de datos ya son de menor costo.

Vamos a ver 2 configuraciones una como configurar Redis y el Caché distribuido de sql server. Para esto dependemos de la interface IDistributedCache.

En IDistributedCache tenemos disponible tanto método sincrónicos como asincrónicos. Esta nos permite agregar, recuperar o eliminar elementos que tengamos en el caché. Veamos los métodos:

  • Get, GetAsync Recupera los datos del caché por medio de una key.
  • Set, SetAsync Agrega un tiem al caché por medio de una key.
  • Refresh, RefreshAsync Actualiza el caché por medio de un key reiniciando el tiempo de expiración.
  • Remove, RemoveAsync Elimina un objeto del caché.

Para poder utilizar la interfaz debemos seguir los siguientes pasos:

  1. Implementar en nuestra aplicación paquete nuget.
  2. Configurar en nuestro archivo startup.cs dentro de ConfigureService para que nos lo agregue en nuestro contenedor.
  3. Por último, agregarlo en los constructores de nuestros controller donde lo vamos a utilizar.

Usando Cache Distribuido Redis

¿Qué es Redis? Redis es un cache distribuido en memoria de origen open-source. Podemos usarlo de forma local o en Azure. Azure provee una implementación llamada Azure Redis Cache.

De la misma forma que antes debemos configurar en nuestro archivo Startup.cs una instancia del RedisDistributedCache para poder tenerla disponibles.


/// <summary>
/// Use Redis Cache in Staging
/// </summary>
/// <param name="services"></param>
public void ConfigureStagingServices(IServiceCollection services)
{

services.AddDistributedRedisCache(options =>
{
options.Configuration = "localhost";
options.InstanceName = "SampleInstance";
});
}

Usando Cache Distribuido con SQL Server

SqlSeverCache nos permite utilizar cache distribuido con soporte en base de datos de Sql Server. Con la herramienta de sql-cache podremos crear una tabla con estructura necesaria para tener este soporte.

C:\SqlCacheSample\>dotnet sql-cache create “Data Source=(localdb)\v11.0;Initial Catalog=DistCache;Integrated Security=True;” dbo DbSQLCache

Como resultado tendremos esta tabla:

Una vez todo listo debemos agregarlos a nuestros servicios de la siguiente manera:


public void ConfigureProductionServices(IServiceCollection services)
{

services.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = @"Data Source=(localdb)\v11.0;Initial Catalog=DistCache;Integrated Security=True;";
options.SchemaName = "dbo";
options.TableName = "TestCache";
});

}

Conclusión

Asp.Net Core posee un gran soporte para la utilización de cache en distribuido tanto sea Redis, Azure Cache Redis o SqlCache el cual nosotros podemos aprovechar para aumentar el rendimiento y la escalabilidad de nuestras aplicaciones. Para tener en cuenta Redis mi elección siempre ya que posee

[Article] Asp.Net Core | Mejorar el rendimiento de nuestras Apps con Cache

Uno de nuestros clientes nos pidió que analicemos su aplicación para que aumente su rendimiento. Lo primero que pensamos es usar caché. Asp.Net Core, como otras versiones, tiene un muy buen soporte de Caché en memoria. Trataremos ese tema a continuación.

Objetivo

El objetivo de esta publicación es tocar las posibles utilizaciones y alcance del manejo de del caché en memoria como también que es y para qué son útiles.
Audiencia

Este documento está dirigido a desarrolladores que quieren conocer Asp.Net Core o personas que desarrollan tareas de consultoría de desarrollo o que simplemente están interesados en leer e investigar sobre la tecnología alcanzada por esta publicación.

Desarrollo

Usar caché puede aumentar significa mente la performance y la escalabilidad de nuestra aplicación reduciendo el costo en la generación de contenido. Una buena recomendación es la utilización de cache en los datos que cambia con poca frecuencia. Por ejemplo, si tenemos datos recuperados de una base de datos que no es modificada nunca o muy pocas veces, podemos guardar en caché estos datos, por un cierto tiempo, sin la necesidad de consultar todo el tiempo la base de datos o su datasource.

Asp.Net Core, como otra versión, tiene diferentes tipos de cache que podemos utilizar. El más común es un caché basado en memoria el cual es el más utilizado. Este caché es almacenado en la memoria del WebServer.

Para usar cache en memoria debemos hacer uso de IMemoryCache primer debemos utilizar el packete nuget “Microsoft.Extensions.Caching.Memory”, luego, esto debemos inyectarlo como servicio. Veamos el siguiente código el siguiente código:

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;

public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMemoryCache();
services.AddMvc();
}

public void Configure(IApplicationBuilder app)
{
app.UseMvcWithDefaultRoute();
}
}

La configuración de nuestro controller deberia ser de la siguiente manera:

public class HomeController : Controller
{
private IMemoryCache _cache;

public HomeController(IMemoryCache memoryCache)
{
_cache = memoryCache;
}

Un método muy util es TryGetValue. Este método nos provee la capacidad de verificar si existe un objeto en el caché por medio de una Key. Si existe nos devuelve como salida el existente.


public IActionResult CacheTryGetValueSet()
{
DateTime cacheEntry;

// Look for cache key.
if (!_cache.TryGetValue(CacheKeys.Entry, out cacheEntry))
{
// Key not in cache, so get data.
cacheEntry = DateTime.Now;

// Set cache options.
var cacheEntryOptions = new MemoryCacheEntryOptions()
// Keep in cache for this time, reset time if accessed.
.SetSlidingExpiration(TimeSpan.FromSeconds(3));

// Save data in cache.
_cache.Set(CacheKeys.Entry, cacheEntry, cacheEntryOptions);
}

return View("Cache", cacheEntry);
}

Unos métodos más simplistas son GetOrCreate(), creo que el nombre lo dice todo y tambien como la gran mayoria de Asp.Net Core tenemos la posibilidad de utilizarlo de un modo asincrónico por medio de GetOrCrareAsyn().

public IActionResult CacheGetOrCreate()
{
var cacheEntry = _cache.GetOrCreate(CacheKeys.Entry, entry =>
{
entry.SlidingExpiration = TimeSpan.FromSeconds(3);
return DateTime.Now;
});

return View("Cache", cacheEntry);
}

public async Task<IActionResult> CacheGetOrCreateAsync()
{
var cacheEntry = await
_cache.GetOrCreateAsync(CacheKeys.Entry, entry =>
{
entry.SlidingExpiration = TimeSpan.FromSeconds(3);
return Task.FromResult(DateTime.Now);
});

return View("Cache", cacheEntry);
}

Nuestro cache en memoria puede ser parametrizado por medio de MemoryCacheEntryOptions. podemos configurar:

  • Un tiempo de experiencia. Es el máximo tiempo que va a mantenerse este y permite renovarlo continuamente.
  • Si el cache es consultado se renueva el tiempo de expiración.
  • Se puede establecer para que nunca se remueva o expire.
  • Por medio de PostEvictionDelegate podemos asociar une evento cuando el cache sea removido.

Veamos los ejemplos en código:

public IActionResult CreateCallbackEntry()
{
var cacheEntryOptions = new MemoryCacheEntryOptions()
// Pin to cache.
.SetPriority(CacheItemPriority.NeverRemove)
// Add eviction callback
.RegisterPostEvictionCallback(callback: EvictionCallback, state: this);

_cache.Set(CacheKeys.CallbackEntry, DateTime.Now, cacheEntryOptions);

return RedirectToAction("GetCallbackEntry");
}

public IActionResult GetCallbackEntry()
{
return View("Callback", new CallbackViewModel
{
CachedTime = _cache.Get<DateTime?>(CacheKeys.CallbackEntry),
Message = _cache.Get<string>(CacheKeys.CallbackMessage)
});
}

public IActionResult RemoveCallbackEntry()
{
_cache.Remove(CacheKeys.CallbackEntry);
return RedirectToAction("GetCallbackEntry");
}

private static void EvictionCallback(object key, object value,
EvictionReason reason, object state)
{
var message = $"Entry was evicted. Reason: {reason}.";
((HomeController)state)._cache.Set(CacheKeys.CallbackMessage, message);
}

Conclusión

Asp.Net Core posee un excelente soporte la utilización de cache en memoria la cual nosotros podemos aprovechar para aumentar el rendimiento de nuestras aplicaciones. En próximo post hablaremos de la utilización de cache distribuido a nivel global y otras técnicas para aumentar el rendimiento de nuestras aplicaciones web.

[Article] Asp.Net Core | Globalización y Localización

En estos últimos días estuve analizando una aplicación para convertirla a soporte en múltiples lenguajes. Asp.Net Core nos brinda una gran cantidad servicios en su middleware. Entre ellos tenemos disponibles para manejar la globalización y la localización de una manera muy simple.

Objetivo

El objetivo de esta publicación es tocar las posibles utilizaciones y alcance del manejo de globalización y localización como también que son y para qué pueden sernos útiles.

Audiencia

Este document o está dirigido a desarrolladores que quieren conocer Asp.Net Core o personas que desarrollan tareas de consultoría de desarrollo o que simplemente están interesados en leer e investigar sobre la tecnología alcanzada por esta publicación.

Desarrollo

Comencemos aclarando algunos conceptos:

  • Globalización (Globalization) es el proceso por el cual se debe diseñar una aplicación para que pueda responder a diferentes culturas. Esta agrega soporte para entrada de información, también su salida o visualizaciones definida para cierto lenguaje o para un área geográfica específica.
  • La localización (Localization) es la adaptación de una aplicación globalizada para una cultura particular cultura.

Para tener disponibles estas caracteristicas deberemos hacer uso de 2 interfaces IStringLocalizar y IStringLocalizar<T>. Estas 2 últimas hacen uso de las clases ResourceManager and ResourceReader que proveerán la información específica que será recuperada de los archivos de recursos.

Algo para destacar es que no es necesario tener los archivos de recursos disponibles. Si invocamos un valor y no existe un archivo de recurso o valor devolverá el nombre del parámetro. Esto puede utilizarse como idioma por default.

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;

namespace AspNetCoreGloLoc.Controllers
{
[Route("api/[controller]")]
public class StringTextController : Controller
{
private readonly IStringLocalizer&lt;StringTextController&gt; _localizer;

public StringTextController(IStringLocalizer&lt;StringTextController&gt; localizer)
{
_localizer = localizer;
}
[HttpGet]
public string Get()
{
return _localizer["Title"];
}
}
}

Si necesitamos implementar globalización y localización dentro de código con html una buena opción es usar las interfaces IHtmlLocalizar y IHtmlLocalizar<T>. Debemos hacer uso de los siguientes namespaces Microsoft.AspNetCore.Localization y Microsoft.AspNetCore.Mvc.Localization. Usualmente usamos siempre texto simple antes que html específico.


using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Localization;&lt;/pre&gt;
namespace Localization.StarterWeb.Controllers
{
public class BookController : Controller
{
private readonly IHtmlLocalizer&lt;BookController&gt; _localizer;

public BookController(IHtmlLocalizer&lt;BookController&gt; localizer)
{
_localizer = localizer;
}

public IActionResult Hello(string name)
{
ViewData["Message"] = _localizer["&lt;b&gt;Hello&lt;/b&gt;&lt;i&gt; {0}&lt;/i&gt;", name];

return View();
}
Una opción interesante es la posibilidad de particionar la localización por controlador, área o algún tipo de contenedor. Veamos cómo podemos usar ShareResource para esto.
namespace Localization.StarterWeb
{
public class SharedResource
{
}
}

Podemos usar Startup del proyecto y agregarlo al contenedor global veamos el siguiente controlador de ejemplo:

&lt;/pre&gt;
public class InfoController : Controller
{
private readonly IStringLocalizer&lt;InfoController&gt; _localizer;
private readonly IStringLocalizer&lt;SharedResource&gt; _sharedLocalizer;

public InfoController(IStringLocalizer&lt;InfoController&gt; localizer,
IStringLocalizer&lt;SharedResource&gt; sharedLocalizer)
{
_localizer = localizer;
_sharedLocalizer = sharedLocalizer;
}

public string TestLoc()
{
string msg = "Shared resx: " + _sharedLocalizer["Hello!"] +
" Info resx " + _localizer["Hello!"];
return msg;
}

Para una vista podemos usar IViewLocalizer. ViewLocalizer implementa la interface anterior y busca en los archivos de recursos dependiendo el path de la vista.

@using Microsoft.AspNetCore.Mvc.Localization

@inject IViewLocalizer Localizer

@{
ViewData["Title"] = Localizer["About"];
}
&lt;h2&gt;@ViewData["Title"].&lt;/h2&gt;
&lt;h3&gt;@ViewData["Message"]&lt;/h3&gt;

&lt;p&gt;@Localizer["Use this area to provide additional information."]&lt;/p&gt;
&lt;pre&gt;

Importane!:

  • Es necesario usar el paquete nuget “Localization.AspNetCore.TagHelpers”.
  • Generalmente usamos una sola localizacion para texto y no HTML.

Otra opción que utilizamos mucho puede ser por medio de DataAnnotations.

public class RegisterViewModel
{
[Required(ErrorMessage = "The Email field is required.")]
[EmailAddress(ErrorMessage = "The Email field is not a valid e-mail address.")]
[Display(Name = "Email")]
public string Email { get; set; }

[Required(ErrorMessage = "The Password field is required.")]
[StringLength(8, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }

[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}

Conclusión

Asp.Net Core posee un de características las cuales nosotros como desarrolladores podemos sacarle un muy buen provecho a la localización y globalización. En próximo post hablaremos de la utilización de archivos de recursos para lograr el mismo objetivo.

[Article] Asp.Net Core | Múltiples entornos de trabajo

Una de las nuevas características de Asp.Net Core es la capacidad de trabajar con múltiples entornos de trabajo. Podemos utilizar un entorno de desarrollo, prueba y producción. Esta configuración nos permitirá modificar el comportamiento según el entorno en el cual está ejecutando la aplicación.

Objetivo

El objetivo de esta publicación es tratar los posibles usos de esta nueva característica junto a las buenas prácticas en el desarrollo de aplicaciones.

Audiencia

Este documento está dirigido a desarrolladores que quieren conocer Asp.Net Core o personas que desarrollan tareas de Consultoría de desarrollo o que simplemente están interesados en leer e investigar sobre la tecnología alcanzada por esta publicación.

Desarrollo

Supongamos que tenemos 3 entornos donde debemos ejecutar o cargar diferentes configuraciones: Development, Staging y Production. Asp.Net Core nos permite distinguir estos entornos por medio de variables denominadas “Enviroment Variable”. la variable a la cual nos referimos se llama ASPNETCORE_ENVIROMENT.  Esta es la que nos permitirá configurar nuestro entorno de trabajo. Los valores más comunes son Development, Staging y Production.

Una vez configurada esta variable podemos detectar en tiempo de ejecución cuál es el valor de esta variable y en qué entorno nos encontramos corriendo la aplicación.

Debemos tener en cuenta que en Linux el nombre es case sensitive, por esta razón, los archivos y las configuraciones serán tomadas como case sensitive como buena práctica.

Development

Este entorno es que usaremos mientras estamos desarrollando nuestra aplicación. ¿Cómo hacemos para configurarlo? debemos hacer botón derecho sobre nuestro proyecto, seleccionar configuración, se abrirá una ventana y seleccionamos de la solapa izquierda “Debug” como podemos ver en la siguiente ventana.

Recordemos que cuando modifico esta pantalla la configuración es modificada en el archivo launchSessting.json dentro de la carpeta properties . En este archivo podemos incluir cualquier entorno que deseemos, por ejemplo, podemos agregar varios perfiles de configuración como podemos ver en el código siguiente:


{

"iisSettings": {

"windowsAuthentication": false,

"anonymousAuthentication": true,

"iisExpress": {

"applicationUrl": "http://localhost:40088/",

"sslPort": 0

}

},

"profiles": {

"IIS Express": {

"commandName": "IISExpress",

"launchBrowser": true,

"environmentVariables": {

"ASPNETCORE_ENVIRONMENT": "Development"

}

},

"IIS Express (Staging)": {

"commandName": "IISExpress",

"launchBrowser": true,

"environmentVariables": {

"ASPNETCORE_ENVIRONMENT": "Staging"

}

}

}

}

Si cambiamos alguna de estas configuraciones en el archivo no se tomarán los efectos automáticamente. Deberemos reiniciar los servidores, sobre todo Kestrel necesitará reiniciar, una vez que se haya modificado para poder detectar correctamente el cambio.

Staging

Este es el entorno que utilizaremos para un entorno de test o preproducción. Casi siempre para un testing final antes de hacer un pasaje a producción. Casi siempre este entorno debería ser un espejo de producción para reducir el impacto de implementación.

Production

Este es el entorno al que muchas veces denominados Vivo. Es un entorno donde configuramos características de seguridad, rendimiento y la fiabilidad de la aplicación. algunas de las características que puede tener el entorno de producción es:

  • Activar funciones de cache
  • Ajustes de archivos del lado del cliente como paquetes, reduccion de archivos js y css o configuraciones de CDN (Content Delivery Network)
  • Desactivar funciones de diagnóstico
  • Activar configuraciones de registro y monitoreo.

La lista puede ser más extensa dependiendo de las necesidades de cada aplicación.

Configurando los entornos

En Windows podemos hacer uso de la configuración por medio de una vez que se está ejecutando nuestra aplicación:

línea de comandos

set ASPNETCORE_ENVIRONMENT=”Development”

PowerShell

$Env:ASPNETCORE_ENVIRONMENT = “Development”

Estos comandos solamente serán validados mientras la ventana donde se está ejecutando este abierta. Si la cerramos perderemos esta configuración. En caso de querer que el valor sea global deberemos configurarlo desde Panel de Control > Sistema > Configuración Avanzada de Sistema, en la solapa Opciones Avanzadas deberemos agregar la variable ASPNETCORE_ENVIRONMENT como vemos en la captura siguiente:

En macOS debemos usar desde el bash el siguiente comando

ASPNETCORE_ENVIRONMENT=Development dotnet run

Ha nivel sistema operativo o maquina debemos configurar las variables en los archivos .bashrc o .bash.profile. debemos editar el archivo y agregar los siguiente:

export ASPNETCORE_ENVIRONMENT=Development

En Linux debemos usar el comando export  desde la consola para la sesión abierta o modificar el archivo bash_profile para todo el sistema operativo o máquina .

Cómo verificar en tiempo de ejecución el entorno

Asp.Net provee un servicio basado en la interface IHostingEnviroment que es disponible por medio de Inyección de Dependencia en el contenedor principal. En el archivo startup.cs podemos ver como es inyectada la variable env  la cual nos permitirá acceder al entorno.


public void Configure(IApplicationBuilder app, IHostingEnvironment env)

{

if (env.IsDevelopment())

{

app.UseDeveloperExceptionPage();

app.UseDatabaseErrorPage();

app.UseBrowserLink();

}

else

{

app.UseExceptionHandler("/Home/Error");

}

Para chequear un entorno específico mientras estamos ejecutando la aplicación, IHostingEnvironment provee el método IsEnviroment( “Nombre del entorno”).

En el código podemos ver que se está preguntando si el entorno es Development por medio del método IsDevelopment() cual cual configurara algunas opciones para este entorno. Por ejemplo, vemos que se configura app.UseBrowserLink(); que es característica propia de visual studio que en producción no usaremos.

También tenemos la posibilidad de usar estas configuraciones por medio de Tag Helper dentro de las Vistas de MVC. Por ejemplo, podemos decirle que utilice archivos css o js no comprimidos en Develoment y que en Staging y Production los use comprimidos.


&lt;environment names="Development"&gt;
&lt;link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" /&gt;
&lt;link rel="stylesheet" href="~/css/site.css" /&gt;
&lt;/environment&gt;
&lt;environment names="Staging,Production"&gt;
&lt;link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.6/css/bootstrap.min.css"
asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" /&gt;
&lt;link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" /&gt;
&lt;/environment&gt;

Conclusión

Asp.Net Core posee un de características las cuales nosotros como desarrolladores podemos sacarle un muy buen provecho como también un gran control sobre los entornos en los cuales trabajamos habitualmente. Esta configuración nos permite cambiar el comportamiento de nuestra aplicación con un simple cambio de valor en la variable de entorno.

[Event] Global Azure BootCamp 2018 | Azure B2C: servicio de identidad ágil para tus desarrollos.

El día de ayer estuvimos presentando Global Azure Bootcamp 2018 Buenos Aires el tema  Azure B2C. Les dejo los link de la presentación y los el video.

El día de ayer estuve presentando Global Azure Bootcamp 2017 Buenos Aires el tema  Azure B2C. Les dejo los link de la presentación y los el video.

Presentación

Video Charla

Video Entrevista

[Article] Azure | Integrando Azure Search en un Website

En en el artículos anterior (ir al articulo anterior) estuvimos viendo qué es Azure Search y como podemos configurarlo para comenzar a utilizarlo. En este artículo veremos cómo integrarlo en una Aplicación Web por medio del SDK que nos brindan.

Objetivo

En esta entrega estaremos integrando Azure Search en una Aplicación Web. Veremos sus características y funcionalidades plasmadas en un WebSite. El código del proyecto podrán descargo desde GitHub desde esta dirección

https://github.com/withoutdebugger/DemoAzureSearchWODv1

Audiencia

Este documento está dirigido a personas que conocen muy poco o nada sobre el tema o personas que desarrollan tareas de consultoría de desarrollo o que simplemente están interesados en leer e investigar sobre la tecnología alcanzada por esta publicación.

Desarrollo

Antes que nada necesitaremos el SDK. El SDK posee un conjunto de librerías de cliente las cuales las utilizaremos para manejar los índices, los datasource, indexadores, subir documentos, ejecutar queries y todo lo que necesitemos.

Las librerías principales son:

  • Azure.Search
  • Search.Models

Estas librerías tienen definidas algunas clases principales como Index, Field y Document como también algunas operaciones como Indexes.Create o Documents.Search asociadas a SearchServiceClient y SerachIndeCliente. Están agrupadas de una forma lógica para una mejor compresión.

Una vez listo el SDK en nuestra aplicación necesitaremos los ApiKey que Azure. Esta llave nos dará acceso al servicio y a base de búsqueda. La guardaremos en nuestra configuración de connections strings.

Para obtenerlos debemos ingresar al portal de Azure. Seleccionamos nuestro servicio de búsqueda -> opción claves:

Estas claves la usaremos dentro de nuestro archivo appsettings.json para tenerlas disponibles y consultarlas en el momento de consumir el servicio.

{
"AzureSearch": {
"SearchServiceName": "{nombre del servicio}",
"SearchServiceAdminApiKey": "{apikey}",
"SearchServiceQueryApiKey": "{apikey} "
},

...

}

Nuestra aplicación es una aplicacion ASP.Net Core MVC junto a una Web APi que será consumida por medio de JQuery. No voy a entrar en detalle de la aplicación MVC pero si en el servicio de búsqueda.

Tendremos el Home Controller que solamente nos devolverá la página inicial donde se encontrarán nuestros métodos en JQuery que consumirán los servicios de búsqueda.

Crearemos una Web API que se llamara Search. Será un controller llamado SearchController. Este recibirá en su constructor la clase IConfiguration para tener disponible el servicio de Azure Search.

{
searchServiceName = _configuration["AzureSearch:SearchServiceName"];
queryApiKey = _configuration["AzureSearch:SearchServiceQueryApiKey"];
indexClient = new SearchIndexClient(searchServiceName, "realestate-us-sample", new SearchCredentials(queryApiKey));
}

Nuestro método principal Get será el encargado de recibir las consultas desde el cliente. En esta primera versión filtra por texto, facets, tag y manejara la paginación. También tendremos algunos métodos privados que nos ayudaran a armar la consulta final.

Modelo Hotel

Este modelo es en el cual se transformara el resultado de Azure Search en nuestra aplicación.

public partial class Hotel
{
[System.ComponentModel.DataAnnotations.Key]
public string listingId { get; set; }
public string description { get; set; }
public string description_es { get; set; }
public string number { get; set; }
public string street { get; set; }
public string city { get; set; }
public string countryCode { get; set; }
public string postCode { get; set; }
public string thumbnail { get; set; }
public string status { get; set; }
public string[] tags { get; set; }
}

Get Method

El método principal recibirá los parámetros de consulta. Dentro podemos ver que tenemos los métodos privados CreateFilterFacets, CreateFilterTags, CreateParameter.

[HttpGet]
public DocumentSearchResul&lt;Hotel&gt; Get(string searchtext = "", string cityFacet = "", string typeFacet = "", string tags = "", string orderby = "", string derbydirection "desc", int actualPage = 1)
{
//Filter
string filter = CreateFilter(cityFacet, typeFacet);

//Filter Tags
IList&lt;ScoringParameter&gt; filterTags = CreatedFilterTags(tags);

//Parameters
SearchParameters sp = CreateParameter(10, filter, orderBy, filterTags, actualPage);

//Search
var result = indexClient.Documents.Search&lt;Hotel&gt;(searchtext, sp);
return result;
}

CreateFilterFacets

 Este método es el encargado de crear los filtros para los facets de City y Type. Como podemos ver en el método, el resultado un string que acompañará a la consulta. Azure Index utiliza una estructura OData formar las consultas. Por ejemplo, en el string el comando podemos ver el comando eq que significa igual. Podemos ver la lista completa de comando desde este link:

https://docs.microsoft.com/en-us/rest/api/searchservice/odata-expression-syntax-for-azure-search

private string CreateFilterFacets(string cityFacet, string typeFacet) {

string filter = "";

if (cityFacet != null)
{
filter += filter != string.Empty ? " and " : "";
filter += "city eq '" + cityFacet + "'";
}

if (typeFacet != null)
{
filter += filter != string.Empty ? " and " : "";
filter += "type eq '" + typeFacet + "'";
}

return filter;
}

CreateFilterTags

Este método es el encargado de crear una lista de Tags para filtrar. A diferencia del anterior, los tags deben filtrarse por medio de ScoringParameter que hace referencia a un nivel de acierto.

private IList&lt;ScoringParameter&gt; CreateFilterTags(string tags) {

IList&lt;ScoringParameter&gt; _parameterTags = new List&lt;ScoringParameter&gt;();

if (tags == null) { return _parameterTags; }

string[] _listTags = tags.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries);

if (_listTags.Count() &amp;gt; 0)
{
_parameterTags.Add(new ScoringParameter("tags", _listTags));
}

return _parameterTags;
}

CreateParameter

Antes de la ejecución final de obtención de resultados debemos armar un SearchParamters. Desde este manejaremos la configuración principal de la consulta como el modo de búsqueda, cuántos tomar por medio de top, el filtro de text, el cálculo para la página, los campos que usaremos, si incluimos el total y los facets que filtraremos. Por último también se agregara tags correspondientes al ScoringParameters.

private SearchParameters CreateParameter(int top, string filter, IList&amp;lt;string&amp;gt; orderby, IList&amp;lt;ScoringParameter&amp;gt; filterTag, int currentPage)
{

SearchParameters searchParameters =
new SearchParameters() {
SearchMode = SearchMode.Any,
Top = 10,
Filter = filter,
Skip = currentPage - 1,
Select = new Lis&lt;String&gt;() { "listingId", "description", "description_es", "number", "street", "city", "countryCode", "postCode", "thumbnail", "status", "tags" },
// Add count
IncludeTotalResultCount = true,
//// Add facets
Facets = new List&lt;String&gt;() { "city", "type", "status", "tags" },
};

searchParameters.OrderBy = orderby;
searchParameters.ScoringParameters = filterTag;

return searchParameters;
}

Ejecución de resultado

Una vez que tenemos todo los objetos armados para nuestra consulta se llamará al siguiente método perteneciente al cliente de Azure Search.

var result = indexClient.Documents.Search&lt;Hotel&gt;(searchtext, sp);

indexClient es del tipo SearchIndexClient el cual es el encargado de realizar finalmente la consulta en nuestro servicio de Azure Search. Podemos ver que le enviaremos nuestra clase Hotel y el se encargara de hacer la transformación.

La aplicación

Finalmente nuestra aplicación se verá visualizará de la siguiente forma luego de una búsqueda:

Podemos filtrar la búsqueda por medio de la selección en cada una de las secciones City, Type y Tags para ajustar nuestros resultados. El encargado de ajustar la búsqueda sera los métodos creados en JQuery.

 

Conclusión

Azure Search es una excelente herramienta para realizar potentes búsquedas. También nos brinda un SDK muy potente para que podamos realizar consultas muy rápidamente y poder así integrar rápidamente en nuestras aplicaciones

Les dejos la aplicación de ejemplo en GitHub para que puedan consultarla:

https://github.com/withoutdebugger/DemoAzureSearchWODv1

También les dejo el link del demo:

http://wodazuresearch.azurewebsites.net/

[Article] Azure | Azure Search

En este artículo hablaremos de Azure Search. Azure Search es una solución que Microsoft nos brinda para poder integrar una excelente experiencia de búsqueda en nuestras aplicaciones móviles, web o cualquier aplicación como empresariales.

Objetivo

En esta entrega veremos qué es Azure Search. También, como configurar nuestra cuenta de Azure Search y un pequeño demo de como utilizarlos en una aplicación web.

Audiencia

Este documento está dirigido a personas que conocen muy poco o nada sobre el tema o personas que desarrollan tareas de consultoría de desarrollo o que simplemente están interesados en leer e investigar sobre la tecnología alcanzada por esta publicación.

Desarrollo

Todos los días, como usuarios, al usar aplicaciones estamos acostumbrados a los métodos de búsqueda rápidos y muy eficientes. Por ejemplo, todos los dias, en algun momento, buscamos en Google o algun motor de búsqueda de nuestra preferencia. Cuando usamos otras aplicaciones buscamos la misma experiencia de búsqueda.

Azure Search nos da la posibilidad de tener la misma experiencia de búsqueda en nuestras aplicaciones. Todo esto es posible gracias que tenemos disponible una gran cantidad de APIs Rest para consumir el servicio, también, un SDK muy completo en .Net. Por último,  dentro del portal de Azure tenemos muchas herramientas para la creación de prototipos, creación y consulta de índices.

Para poder comenzar es necesario una cuenta de Azure. Les dejo el link para poder obtener una cuenta gratuita junto a 200 dolares de crédito para poder probar las capacidades que Azure Search brinda:

Suscribirse

Características de Azure Search

A continuación analizaremos las características funcionales más interesantes, desde mi punto de vista, que podemos usar con Azure Search. Son las siguientes:

Búsqueda de texto completo y analisis de texto

Corresponde a la búsqueda que se basa escribiendo en una caja de texto. Podemos usar operadores lógicos o frases de búsqueda como cualquier buscador de internet.

Integración de datos

Azure Search soporta gran variedad de orígenes de datos. La unica caracteristica que tienen que tener estos datos es que tienen que respetar la estructura de datos del tipo JSON.

No obstante tenemos disponible soporte para Azure SQL Database, Azure Cosmos DB o Azure Blob Storage. En este último podemos tener documentos del tipo Microsoft Office, PDF o Html los cuales pueden ser indexados para búsquedas dentro de documentos.

Análisis Lingüístico

Básicamente, tendremos la capacidad de analizar la busqueda relacionado con una fonética de un lenguaje o alguna expresión regular. Podemos controlar el idioma, los tiempos verbales, género, nombres plurales o singulares o inclusive el manejo de palabras compuestas.

Características de la experiencia del usuario

Entre las características más importantes que podemos brindarles a nuestros usuarios tenemos:

  • Sugerencias de búsqueda: a medida que los usuarios van escribiendo su búsqueda le podemos ir sugerencias mostrando relacionadas con lo ingresado.
  • Navegación por facetas: Es un listado de categorización que podemos usar como filtros. Este tipo de filtro es muy similar a los sitios de compras, como amazon, que en el lado izquierdo tenemos varias opciones agrupadas que al hacer un solo click filtran la búsqueda principal.
  • Resaltado de referencias: podemos aplicarle un formato al resultado, por ejemplo subrayarlo en negrita, indicando que está relacionado con la búsqueda.
  • Ordenación: podemos ofrecer varias forma de ordenar la información obtenida del resultado de búsqueda.
  • Paginación: Como todo buen listado de búsqueda, podemos paginarlo.

Supervisión e informes

Es importante analizar las búsquedas de nuestros usuarios con el objetivo de tener que ajustar nuestro índices para que las posteriores búsquedas sean más efectivo y eficiente. Azure Search registra todas la búsquedas realizadas para posterior análisis. En un post futuro tocaremos este tema con más detalle.

Otras Características

Además de las nombradas, Azure Search tiene las siguientes características:

  • Georeferencia: Filtro por ubicaciones geográficas.
  • Relevancia: Usando perfiles de puntuación podemos darle relevancia a ciertos documentos.
  • Herramientos de prototipos e Inspección: configurar indexadores, crear índices, consulta de un índice particular, etc.

Manos a la Obra

Una vez que tenemos nuestra cuenta Azure, es muy sencillo crear una cuenta de Azure Search. Debemos seguir los siguiente pasos:

1. Ingresar al portal y buscar en el MarketPlace “Búsqueda de Azure”, lo seleccionamos y presionamos crear.

2. Completamos los datos que nos solicita el formulario. En mi caso la url sera testwod pero pueden seleccionar un nombre que ustedes deseen. En el plan de tarifa, para motivos de este demo, usaremos la tarifa gratis.

3. Un vez creado, vamos a grupo de recursos, seleccionamos el nombre de recurso que utilizamos y dentro podemos ver la cuenta de Azure Search que creamos. Le hacemos clic y veremos la información necesaria de nuestra cuenta.

Volvamos a nuestro panel de Azure Search. Para conectar nuestra base de datos debemos seleccionar desde la barra de opciones “Importar Datos” y seleccionamos Muestras para nuestro demo:

Luego de vincularlo veremos la pantalla de configuración de nuestros índices que descubrirá, desde nuestro origen de datos. Vamos a dejarlos por default.

Por último, seleccionamos un nombre para nuestro índice.  El tiempo el cual queremos que se refresque nuestros índices es configurable. Cuales son retornables, cuales filtrables, por cual vamos a ordenar, etc. Para esto último debemos habilitarlo en el datasource como dice en el mensaje. Por el momento no será necesario.

Ahora, una vez que el indexador termine podremos ingresar al explorador de búsqueda para probar algunas consultar.

En la imagen podemos ver que tenemos un cuadro donde podemos ingresar nuestra consulta, como también, un cuadro donde podemos obtener la url de esa búsqueda. Por último, un cuadro con el resultado de la búsqueda.

Vamos a escribir la siguiente consulta y veremos los resultado que se corresponden a nuestros parámetros:

search=granite&amp;country=us

Conclusión

Azure Search es una excelente herramienta para realizar potentes búsquedas desde nuestros orígenes de datos existentes. No es necesario crear una plataforma para esto. Azure Search pone a nuestra disponibilidad la misma experiencia de usuario y la misma potencia de un motor búsqueda especializado para nuestra aplicaciones.

En este post hemos hablamos sobre Azure Search, sus características, como configurar nuestra cuenta y configurar nuestros primeros índices para hacer pruebas. En el próximos post integraremos estos índices a una aplicación en .Net para poder explorar un poco más las capacidades en un sitio web.