Introducción

Azure WebJobs es un servicio de Microsoft Azure que pertenece al conjunto Azure Web App Service que nos permite ejecutar una serie de operaciones en segundo plano como tareas programadas o continuas. Ejemplo: Nos permite programar el envío de correos como newsletters.


Tutorial

En este tutorial voy a explicar el proceso de creación de un WebJob, cómo asociarlo a un App Service y cómo configurar la ejecución del Web Job.

Web Job

Nuestro Web Job se encargará de leer un RSS y guardar la información en nuestra base de datos.

El RSS que vamos a consumir es del periódico El Mundo: https://e00-elmundo.uecdn.es/elmundo/rss/espana.xml

Primero accedemos al RSS mediante el navegador, consultamos la estructura y elegimos los datos que queremos guardar en nuestra base de datos.

Los artículos de cada noticia están envueltos en la etiqueta item

Imagen1

En nuestra base de datos creamos una tabla con los datos a guardar, en mi caso, son el título, la descripción y el enlace del artículo. También añadimos la columna Id, que será la clave primaria y la columna Date que nos será útil para comprobar el funcionamiento del WebJob.

CREATE TABLE ELMUNDONEWS(
	ID int NOT NULL PRIMARY KEY,
	TITLE nvarchar(MAX),
	DESCRIPTION NVARCHAR(MAX),
	LINK NVARCHAR(MAX),
	DATE DATE
)

Creamos una aplicación de consola, que será nuestro WebJob.

Imagen2

Creamos la carpeta Models y dentro la clase NewsRss que contendrá las propiedades que vamos a leer de cada item del RSS. Esta clase nos permitirá recuperar los ítems del RSS para luego convertirlos a otro modelo para guardarlo a nuestra base de datos.

public class NewsRss
    {
        public String Title { get; set; }
        public String Description { get; set; }
        public String Link { get; set; }
    }

Instalamos los siguiente nugget para realizar la conexión con la base de datos.

Imagen3
Imagen4

Creamos el modelo NewsBdd y lo mapeamos.

[Table("ELMUNDONEWS")]
    public class NewsBdd
    {
        [Key]
        [Column("ID")]
        public int Id { get; set; }
 
        [Column("TITLE")]
        public String Title { get; set; }
 
        [Column("DESCRIPTION")]
        public String Description { get; set; }
 
        [Column("LINK")]
        public String Link { get; set; }
 
        [Column("DATE")]
        public DateTime Date { get; set; }
    }

Creamos la carpeta Data y dentro el contexto.

public class WebJobContext: DbContext
    {
        public WebJobContext(DbContextOptions<WebJobContext> options) 
            : base(options) { }
 
        public DbSet<NewsBdd> News { get; set; }
    }

Creamos el repositorio, inyectamos el contexto y creamos los siguientes métodos.

  • GetNesRss: Leerá los ítems del RSS y devolverá una lista de objetos NewsRss
  • SaveNewsBDD: Llamará al método anterior y convertirá los objetos NewsRss a NewsBdd para guardarlos
public class RepositoryRss
    {
        private WebJobContext context;
 
        public RepositoryRss(WebJobContext context)
        {
            this.context = context;
        }
 
        public List<NewsRss> GetNewsRss()
        {
            String url = "https://e00-elmundo.uecdn.es/elmundo/rss/espana.xml";
            XDocument docxml = XDocument.Load(url);
            var query = from data in docxml.Descendants("item")
                        select new NewsRss 
                        { 
                            Title = data.Element("title").Value,
                            Description = data.Element("description").Value,
                            Link = data.Element("link").Value
                        };
            return query.ToList();
        }
 
        public void SaveNewsBDD()
        {
            List<NewsRss> news = this.GetNewsRss();
            int maxId = this.GetMaxId() + 1;
            foreach (NewsRss n in news)
            {
                NewsBdd newBdd = new NewsBdd();
                newBdd.Id = maxId;
                newBdd.Title = n.Title;
                newBdd.Description = n.Description;
                newBdd.Link = n.Link;
                newBdd.Date = DateTime.Now;
                maxId++;
                this.context.News.Add(newBdd);
            }
            this.context.SaveChanges();
        }
 
        private int GetMaxId()
        {
            if (this.context.News.Count() == 0)
                return 0;
            else
                return this.context.News.Max(x => x.Id);
        }
    }

Como estamos en un proyecto de consola instalamos el siguiente nuget para realizar las inyecciones de dependencias.

Imagen5

En program establecemos la conexión a la base de datos, inyectamos las dependencias y ejecutamos el método SaveNewsBdd.

class Program
    {
        static void Main(string[] args)
        {
            String sqlCon = "Data Source=sqltajamargnp.database.windows.net;Initial Catalog=azuretajamar;Persist Security Info=True;User ID=admingnp;Password=****";
            var provider = new ServiceCollection()
                .AddTransient<RepositoryRss>()
                .AddDbContext<WebJobContext>(options =>
                    options.UseSqlServer(sqlCon)).BuildServiceProvider();
            RepositoryRss repo = provider.GetService<RepositoryRss>();
 
            Console.WriteLine("Alimentando BDD...");
            repo.SaveNewsBDD();
            Console.WriteLine("Proceso terminado");
        }
    }

Si ejecutamos el proyecto nuestra tabla contendrá 63 registros nuevos.

Imagen6

App Service

Finalizado el WebJob, procedemos a la creación de nuestro App Service, que será un proyecto ASP.Net Core que usará un patrón Mvc. Este proyecto se encargará de leer los datos de nuestra tabla.

Imagen7

Instalamos los siguientes Nuggets.

Imagen8
Imagen9

Dentro de Models creamos la clase News y la mapeamos.

[Table("ELMUNDONEWS")]
    public class News
    {
        [Key]
        [Column("ID")]
        public int Id { get; set; }
 
        [Column("TITLE")]
        public String Title { get; set; }
 
        [Column("DESCRIPTION")]
        public String Description { get; set; }
 
        [Column("LINK")]
        public String Link { get; set; }
 
        [Column("DATE")]
        public DateTime Date { get; set; }
    }

Dentro de Data creamos el contexto.

public class NewsContext : DbContext
    {
        public NewsContext(DbContextOptions<NewsContext> options)
        : base(options) { }
        public DbSet<News> News { get; set; }
    }

Dentro de Repositories creamos el repositorio y obtenemos los registros.

public class RepositoryNews
    {
        private NewsContext context;
 
        public RepositoryNews(NewsContext context)
        {
            this.context = context;
        }
 
        public List<News> GetNews()
        {
            return this.context.News.ToList();
        }
    }

En un controlador, en mi Home, inyectamos el repositorio y cargamos los registros en una vista.

public class HomeController : Controller
    {
        private RepositoryNews repo; 
 
        public HomeController(RepositoryNews repo)
        {
            this.repo = repo;
        }
 
        public IActionResult Index()
        {
            return View(this.repo.GetNews());
        }
    }

Creamos la vista y pintamos los datos.

@model List<News>
 
<h1 class="display-1 text-center">Noticias</h1>
 
<div class="table-responsive">
    <table class="table table-hover">
        <thead>
            <tr>
                <th>#</th>
                <th>Titulo</th>
                <th>Descripción</th>
                <th>Enlace</th>
                <th>Fecha de modificación</th>
            </tr>
        </thead>
        <tbody>
            @foreach (News n in Model)
            {
            <tr>
                <td>@n.Id</td>
                <td>@n.Title</td>
                <td>@n.Description</td>
                <td>
                    <a href="@n.Link">Enlace</a>
                </td>
                <td>@n.Date</td>
            </tr>
            }
        </tbody>
    </table>
</div>

Finalizado el proyecto, lo subimos a Azure como una App Service, para ello hacemos Click Derecho sobre el proyecto y le damos a Publish.

Imagen10

Ya tenemos el app service subido, ahora tenemos que asociarle el WebJob, para ello volvemos a la aplicación de consola.

Sobre el proyecto hacemos click derecho y le damos a Open Folder in File Explorer.

Imagen11

Se nos abirá la carpeta de nuestro proyecto, vamos a bin/Debug. Nos interesa la carpeta netcoreapp que es la que contiene el ejecutable de la aplicación.

Imagen12

Comprimimos la carpeta en un fichero zip, no puede ser rar ni otro tipo de extensión.

Imagen13

Abrimos el portal de Azure, localizamos nuestro App Service y buscamos la sección de Trabajos Web.

Imagen14

Agregamos un nuevo recurso dándole a Agregar.

Le damos un nombre, subimos el fichero comprimido y seleccionamos el tipo de ejecución.

Si seleccionamos Continuo, se ejecutará constantemente.

Vamos a seleccionar desencadenado y mediante una expresión CRON indicamos que queremos que se ejecute cada minuto.

0 */1 * * * *

Le damos a aceptar y se ejecutará una vez, después tendremos que esperar el tiempo indicado.

Comprobamos nuestra table, veremos que cada minuto tenemos más registros.

SELECT COUNT(ID) FROM ELMUNDONEWS


Conclusión

Los WebJobs son un servicio bastante útil que nos permite optimizar nuestro trabajo cuando dependemos de tareas en segundo plano.

Es muy útil si lo vinculamos a un Service Bus, permitiéndonos realizar cualquier tarea que se ejecutaría al reaccionar a un evento.


Autor/a: Gerard Alexander Nina Picón

Curso: Desarrollo Web Full Stack, MultiCloud y Multiplataforma
Centro: Tajamar
Año académico: 2020-2021
Código: https://github.com/GerardNP/WebJobPost

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.