¿Qué es un Web Job?

Azure WebJobs es un servicio de Microsoft Azure que pertenece al conjunto de Azure Web App Service, y cuya funcionalidad nos permite ejecutar acciones de nuestra aplicación que se encuentran en un segundo plano como, por ejemplo: procesamiento de imágenes, procesamiento de ítems de una cola, lectura de RSS, mantenimiento de archivos, y envío de correos electrónicos. Dentro del portal de Azure podremos ejecutar estas operaciones como tareas continuas, on demand o programadas.

  • Tareas continuas: las operaciones se ejecutan automáticamente y de forma continua.
  • Tareas On Demand: las operaciones se ejecutan manualmente desde el portal de Azure.
  • Tareas programadas: las operaciones se ejecutarán periódicamente, cuyo tiempo de ejecución será programado a nuestra conveniencia hasta una fecha de caducidad determinada o indefinidamente.

TUTORIAL

En este tutorial voy a explicar paso por paso la creación de una aplicación de consola desde Visual Studio, la cual será nuestro WebJob que cargará los chollos más actualizados de la página chollometro.com mediante la lectura de su RSS, y seguidamente almacenaremos estos datos en una bbdd SQL. Posteriormente, asociaremos este WebJob a una aplicación ASP.NET MVC donde mostraremos los chollos. Y por último, subiremos la aplicación a Azure y configuraremos el WebJob, primero como tarea on demand para ejecutarlo manualmente desde el portal de Azure, y después como tarea programada para que se ejecute cada 5 minutos.

Lo primero que vamos a hacer es crearnos la tabla Chollos dentro de nuestra base de datos.

CREATE TABLE [dbo].[CHOLLOS](
            [IDCHOLLO] [int] NOT NULL PRIMARY KEY,
            [TITULO] [nvarchar](max) NULL,
            [ENLACE] [nvarchar](max) NULL,
            [DESCRIPCION] [nvarchar](max) NULL,
            [FECHA] [date] NULL,
            [CATEGORIA] [nvarchar](max) NULL)

Ahora nos creamos nuestra aplicación de consola WebJobChollos.

Dentro creamos una carpeta Models y añadimos la clase CholloRss con las propiedades que deseemos leer de cada “ítem” del RSS. En este caso he creado los campos Titular, Enlace, Descripcion y Categoria.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace WebJobChollos.Models
{
    public class CholloRss
    {
        public String Titular { get; set; }
        public String Enlace { get; set; }
        public String Descripcion { get; set; }
        public String Categoria { get; set; }
    }
}

Agregamos el Nuget de EntityFramework

A continuación, sobre la misma carpeta Models agregamos otra clase llamada CholloBbdd, que será la encargada de mapear los datos con la bbdd.



using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace WebJobChollos.Models
{
    [Table("CHOLLOS")]
    public class CholloBbdd
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        [Column("IDCHOLLO")]
        public int IdChollo { get; set; }
 
        [Column("TITULO")]
        public String Titular { get; set; }
 
        [Column("ENLACE")]
        public String Enlace { get; set; }
 
        [Column("DESCRIPCION")]
        public String Descripcion { get; set; }
 
        [Column("FECHA")]
        public DateTime Fecha { get; set; }
 
        [Column("CATEGORIA")]
        public String Categoria { get; set; }
    }
}

Creamos una carpeta llamada Repositories y añadimos la clase RepositoryRss con el método GetChollos() que leerá el documento xml del servicio RSS.

En la consulta del documento creamos un objeto CholloRss por cada “ítem” que leemos del xml, donde rellenamos las propiedades Titulo, Enlace, Descripcion, y Categoria del mismo objeto con los elementos correspondientes de cada “ítem”. Por último, este método devolverá una lista de chollos que utilizaremos más adelante.



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using WebJobChollos.Models;
 
namespace WebJobChollos.Repositories
{
    public class RepositoryRss
    {
        public List<CholloRss> GetChollos()
        {
            String url = "https://www.chollometro.com/rss";
            XDocument docxml = XDocument.Load(url);
            var consulta = from datos in docxml.Descendants("item")
                           select new CholloRss
                           {
                               Titular = datos.Element("title").Value,
                               Enlace = datos.Element("link").Value,
                               Descripcion = datos.Element("description").Value,
                               Categoria = datos.Element("category").Value
                           };
            return consulta.ToList();
        }
    }
}

Ahora nos creamos la carpeta Data y añadimos la clase ChollosContext que será la clase encargada comunicarse con la base de datos. Hereda de DbContext.

 
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WebJobChollos.Models;
 
namespace WebJobChollos.Data
{
    public class ChollosContext:DbContext
    {
        public ChollosContext() : base("name=cadenachollos") { }
        public DbSet<CholloBbdd> Chollos { get; set; }
 
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            Database.SetInitializer<ChollosContext>(null);
        }
    }
}

En el App.Config, dentro de <connectionStrings> añadimos la cadena de conexión a la base de datos.

Vamos a la carpeta Repositories, y creamos una nueva clase llamada RepositoryBbdd que se encargará de almacenar los chollos del RSS en nuestra base de datos de SQL.

En ella creamos el método ActualizarChollos(), y le pasamos como parámetro la lista de chollos que hemos leído del RSS en el RepositoryRss. Realizamos un foreach para recorrer toda la lista de chollos. Dentro creamos un objecto CholloBbdd por cada vuelta del foreach, e igualamos las propiedades de dicho objeto a las de cada chollo almacenado en la lista, además de crear otras nuevas como el Id que se autoincrementará con el método GetMaxIdChollo(), y la fecha que la igualamos al método DateTime.Now() para obtener el día en el que estamos. Por último, añadimos cada chollo a la tabla Chollos de SQL y guardamos los cambios.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WebJobChollos.Data;
using WebJobChollos.Models;
 
namespace WebJobChollos.Repositories
{
    public class RepositoryBbdd
    {
        ChollosContext context;
 
        public RepositoryBbdd()
        {
            this.context = new ChollosContext();
        }
 
        public int GetMaxIdChollo()
        {
            var consulta = this.context.Chollos;
            if (consulta.Count() == 0)
            {
                return 1;
            }
            else
            {
                return consulta.Max(z => z.IdChollo) + 1;
            }
        }
 
        public void ActualizarChollos(List<CholloRss> chollos)
        {
            int id = this.GetMaxIdChollo();
            foreach (CholloRss chollo in chollos)
            {
                CholloBbdd chollobbdd = new CholloBbdd();
                chollobbdd.IdChollo = id;
                chollobbdd.Fecha = DateTime.Now;
                chollobbdd.Titular = chollo.Titular;
                chollobbdd.Descripcion = chollo.Descripcion;
                chollobbdd.Categoria = chollo.Categoria;
                chollobbdd.Enlace = chollo.Enlace;
                id += 1;
                this.context.Chollos.Add(chollobbdd);
            }
            this.context.SaveChanges();
        }
    }
}
 

A continuación, dentro de la clase Program será donde realicemos las llamadas correspondientes y comprobaremos que todo es funcional.

Instanciamos el RepositoryRss, y a partir de él accedemos al método GetChollos() que nos devolverá una lista de chollos. Seguidamente realizamos un foreach de dicha lista para mostrar el titulo de cada chollo en la aplicación de consola, algo meramente visual. Por último, instanciamos el RepositoryBbbdd para acceder al método ActualizarChollos(), y le pasamos como parámetro la lista de chollos para que nos almacene los datos en SQL.

 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WebJobChollos.Models;
using WebJobChollos.Repositories;
 
namespace WebJobChollos
{
    class Program
    {
        static void Main(string[] args)
        {
            RepositoryRss repo = new RepositoryRss();
            List<CholloRss> chollos = repo.GetChollos();
            foreach (CholloRss chollo in chollos)
            {
                Console.WriteLine(chollo.Titular);
            }
            RepositoryBbdd repobbdd = new RepositoryBbdd();
            repobbdd.ActualizarChollos(chollos);
            Console.WriteLine("Chollos actualizados en BBDD");
        }
    }

Aquí vemos todos los chollos almacenados en la bbdd.

Una vez configurado nuestro WebJob procedemos a crearnos una aplicación ASP.NET MVC para mostrar los chollos de la base de datos.

IMPORTANTE: QUITAR LAS LINEAS DE Console.ReadLine() EN NUESTRA CLASE PROGRAM

Sobre la misma solución del proyecto del WebJob creamos un nuevo proyecto llamado MvcChollos con la plantilla MVC.

Establecemos este proyecto como principal dentro de nuestra solución y añadimos el NuGet de EntityFramework.

Sobre la carpeta Models agregamos la clase Chollo y la mapeamos para recuperar la información de cada chollo de la base de datos. Podemos copiar y pegar de la clase CholloBbdd de nuestro WebJobChollos ya que va a ser idéntica a esta.


using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Web;
 
namespace MvcChollos.Models
{
    [Table("CHOLLOS")]
    public class Chollo
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        [Column("IDCHOLLO")]
        public int IdChollo { get; set; }
 
        [Column("TITULO")]
        public String Titular { get; set; }
 
        [Column("ENLACE")]
        public String Enlace { get; set; }
 
        [Column("DESCRIPCION")]
        public String Descripcion { get; set; }
 
        [Column("FECHA")]
        public DateTime Fecha { get; set; }
 
        [Column("CATEGORIA")]
        public String Categoria { get; set; }
    }
}

Ahora tenemos que añadir un contexto llamado ChollosContext para que nuestra aplicación se conecte a la cadena de conexión de la base de datos. Podemos copiar y pegar del contexto de nuestro WebJobChollos ya que va a ser idéntico a este.


using MvcChollos.Models;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;
 
namespace MvcChollos.Data
{
    public class ChollosContext : DbContext
    {
        public ChollosContext() : base("name=cadenachollos") { }
        public DbSet<Chollo> Chollos { get; set; }
 
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            Database.SetInitializer<ChollosContext>(null);
        }
    }
}

También hay que añadir en el Web.config la connectionString de nuestra cadena de conexión. La podemos copiar y pegar del proyecto WebJobChollos ya que será la misma.

A continuación, añadiremos una carpeta Repositories, y dentro de ella la clase RepositoryChollos. En esta clase realizamos una consulta para recuperar los chollos de la base de datos.


using MvcChollos.Data;
using MvcChollos.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
 
namespace MvcChollos.Repositories
{
    public class RepositoryChollos
    {
        ChollosContext context;
        public RepositoryChollos()
        {
            this.context = new ChollosContext();
        }
 
        public List<Chollo> GetChollos()
        {
            var consulta = from datos in context.Chollos
                           orderby datos.IdChollo descending
                           select datos;
            return consulta.ToList();
        }
    }
}
 

Ahora añadimos un nuevo controlador llamado ChollosController sobre la carpeta Controllers. Dentro de este controlador lo único que hacemos es mandar la lista de chollos que recuperamos del repositorio a la vista Index.

 
using MvcChollos.Repositories;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
 
namespace MvcChollos.Controllers
{
    public class ChollosController : Controller
    {
        RepositoryChollos repo;
 
        public ChollosController()
        {
            this.repo = new RepositoryChollos();
        }
        public ActionResult Index()
        {
            return View(this.repo.GetChollos());
        }
    }
}

Y creamos la vista Index donde hacemos el diseño de la lista de chollos a nuestro gusto.

 
@using MvcWebJob.Models
@model List<Noticia>
@{
    ViewBag.Title = "Index";
}
 
<h2>Últimas noticias</h2>
<div class="row">
    @foreach(Noticia not in Model)
    {
        <div class="col-md-6">
            <h2 style="color: blue">@not.IdTitular | @not.Titular</h2>
            <h3>@not.Fecha</h3>
            <p>
                <a href="@not.Enlace" target="_blank">
                    @not.Enlace
                </a>
            </p>
            <p>@Html.Raw(not.Descripcion)</p>
        </div>
        <hr/>
    }
</div>

Una vez terminada la aplicación MVC tenemos que asociarla al Web Job.  Para ello, debemos indicar que la aplicación de consola WebJobChollos será una tarea de trabajo de la aplicación MvcChollos.

Sobre el proyecto MvcChollos clicamos en “Add” y “Existing Project as Azure WebJob” .

En “Project Name” seleccionamos nuestro proyecto y en “WebJob run mode” lo cambiamos a “Run on Demand” para poder ejecutar el WebJob manualmente desde Azure cuando queramos. Si lo dejásemos por defecto en “Run Continuosly” el Web Job se ejecutaría continuamente. Y por último le damos a “OK”.

Nos instalará automáticamente el paquete NuGet de WebJobs.

A continuación, ya podemos publicar nuestro proyecto MvcChollos en Azure.

Hacemos click derecho en el proyecto y seleccionamos “Publish”.

Se nos abre una ventana nueva y seleccionamos “Create New” y le damos a “Publish”.

Seguidamente configuramos el App Service que vamos a crear y cuando esté todo listo clicamos en “Create”.

Desde el explorador de servidor si entramos en el recurso de nuestra aplicación MvcChollos, podemos comprobar si hemos configurado el WebJob correctamente. Como vemos el WebJob está dentro de On Demand tal y como lo hemos configurado anteriormente.

Ahora si entramos en el portal de Azure y accedemos a los Trabajos web de nuestro recurso veremos nuestro WebJob como “Desencadenado”.

Cada vez que ejecutemos el WebJob se cargarán los chollos más actualizados en la aplicación web.

Ahora vamos a programar nuestro WebJob para que cargué los chollos automáticamente cada 5 minutos.

Primero hacemos click derecho sobre el proyecto WebJobChollos (la aplicación de consola) y seleccionamos “Open Folder in File Explorer” para acceder a la carpeta del proyecto.

A continuación, entramos en la carpeta “bin” y hacemos un zip de la carpeta “Debug” que es donde se encuentra el archivo .exe del WebJobChollos.

IMPORTANTE: TIENE QUE SER ZIP YA QUE NO RECONOCE OTRO FORMATO

Ahora volvemos al portal de Azure y dentro de Trabajos Web del recurso MvcChollos clicamos en “Agregar” y se abrirá una ventana nueva a la derecha donde configuraremos el WebJob programado.

  • Nombre: WebJobChollosProgramado
  • Carga de archivos: “Debug.zip” (el zip que hemos creado anteriormente)
  • Tipo: Desencadenados
  • Desencadenadores: Programado
  • Expresión CRON: aquí le diremos cada cuanto tiempo queremos que se ejecute el WebJob mediante una expresión CRON. En este enlace de Microsoft podemos ver como se realiza una expresión CRON según el intervalo de tiempo que queramos. En este caso la expresión será la siguiente (cada 5 minutos): “0 */5 * * * *”.

Cuando lo tengamos todo listo le damos a “Aceptar”, y ya tendremos configurado nuestro WebJob automatizado para que muestre chollos periódicamente.

Por último, le damos a «Ejecutar«

Y vemos que en el Estado pone «En ejecución«, por lo que el WebJob ya se ejecutará automáticamente cada 5 minutos.

CONCLUSIÓN

En definitiva, WebJob es un servicio de Azure bastante útil que nos ayuda a optimizar nuestro trabajo cuando desarrollamos una aplicación web que depende de tareas en segundo plano. Por ejemplo, en este caso, no tenemos que estar cada día actualizando la base de datos manualmente para mostrar los chollos más actualizados, sino que el WebJob lo hace por nosotros.

Autor: Carlos Busón Matutano

Curso: Microsoft MCSA Web Applications + Microsoft MCSD App Builder + Xamarin

Centro: Tajamar

Año académico: 2018-2019

GitHub: https://github.com/cbumat/WebJobChollos

Fichero .rar:
https://www.mediafire.com/file/ib20gbh4esa5n8c/WebJobChollos.rar/file

Leave a Comment

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.