¡Hola a todos! Bienvenidos post tutorial donde aprenderemos como utilizar Azure Cosmos DB, pero antes de empezar, hablaremos qué es Azure Cosmos DB.

¿Qué es Azure Cosmos DB?

Azure Cosmos DB es un servicio de base de datos con varios modelos distribuido de forma global de Microsoft. Cosmos DB permite escalar de forma elástica e individual el rendimiento y el almacenamiento en cualquier número de regiones de Azure a nivel mundial. Puede escalar de forma elástica el rendimiento y almacenamiento, y sacar provecho del rápido acceso a datos mediante la API que prefiera, entre las que se incluyen SQL, MongoDB, Cassandra, Tables o Gremlin.

Una vez que sabemos esto, ya nos podemos hacer una idea al menos de qué es y cómo va a funcionar. ¡Empecemos!

Creación del recurso en el Portal de Azure

Nada más entrar en el portal de Azure veremos una barra a nuestra izquierda en el que veremos varias opciones. Elegiremos la opción «Crear un recurso».

Ahora nos saldrá una sección con dos listas llamadas Azure Marketplace y Popular. En la lista Popular vermos la opción Azure Cosmos DB y la seleccionamos.

1Cosmos

Una vez hecho esto, nos saldrá la siguiente ventana.

2Cosmos

Tendremos que rellenar los datos que nos piden, en mi caso lo he rellenado de la forma que veis en pantalla.

Ahora nos dirigimos a nuestro recurso creado y en la parte de la izquierda vemos otra vez una lista de opciones, buscamos la opción claves. En esta sección nos proporcionarán la ruta y las claves para poder usar nuestro recurso en el proyecto. Copiamos el URI y la CLAVE PRINCIPAL.

3Cosmos

Aplicación con Cosmos DB

Ya hecho todos los pasos anteriores, crearemos una aplicación Web Core MVC. Para ello vamos a File<new<Project. Seleccionamos la opción Web y elegimos ASP .NET Core Web Application. Llamaremos al proyecto «ProyectoCosmosDb».

4Cosmos

A continuación elegimos la opción «Web Application (Model-View-Controller)».

5Cosmos

Una vez creado el proyecto nos dirigimos al archivo «appsettings.json» y escribimos lo siguiente:

"CosmosDb": {
    "endpoint": "AQUI LA URI",
    "primarykey": "AQUI LA CLAVE PRINCIPAL"
  }

El archivo debería de quedar de esta manera:

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*",
  "CosmosDb": {
   "endpoint": "AQUI LA URI",
    "primarykey": "AQUI LA CLAVE PRINCIPAL"
  }
}

Lo siguiente que haremos es instalar los Nuggets para comunicarnos con Cosmos DB. Para ello damos click derecho sobre nuestro proyecto y seleccionamos la opción «Manage NuGet Packages«.

6Cosmos

Buscamos el siguiente paquete en la sección Browse:

7Cosmos

Una vez instalado ya nos dejamos de preocupar por la configuración de la aplicación y empecemos a crear contenido.

Lo primero que haremos es crear un modelo llamado Pelicula. Este modelo es una clase que estará en la carpeta Models. Para crear la clase damos click derecho sobre la carpeta «Models» y elegimos la opción «Add» y seleccionamos «Class…» y lo llamamos «Pelicula».

La clase «Pelicula» tiene el siguiente código:

public class Pelicula
    {
        [JsonProperty(PropertyName = "id")]
        public String Id { get; set; }
        public String Titulo { get; set; }
        public String TituloOriginal { get; set; }
        public String Sinopsis { get; set; }
        public String Genero { get; set; }
        public String Imagen { get; set; }

    }

La decoración «JsonProperty» sirve para que coja como «primary key» el id.

RepositoryCosmosDB

Lo siguiente que haremos es crear una carpeta llamada Repositoryies sobre el proyecto, para ello damos click derecho sobre el proyecto, Add y seleccionamos «New folder…«.

Sobre la carpeta Repositories creamos una clase llamada RepositoryCosmosDb.

IMPORTANTE: En las siguientes clases a veces nos saldrá un subrayado en rojo aunque esté bien escrito, eso es porque no se está haciendo un uso de la clase o dependencia. Para ello seleccionamos lo que esté en subrayado en rojo, pulsamos la tecla ALT + ENTER y seleccionamos la opción «using«.

Dentro de la clase RepositoryCosmosDb creamos las siguientes variables:

IConfiguration configuration;
String bbdd;
String collection;
DocumentClient client;

Lo siguiente que haremos es un constructor de la clase para darle valores a las variables.

public RepositoryCosmosDb(IConfiguration configuration)
{
      this.configuration = configuration;
      this.bbdd = "PeliculasBBDD";
      this.collection = "PeliculasCollection";
      String endpoint = configuration["CosmosDb:endpoint"];
      String primarykey = configuration["CosmosDb:primarykey"];
      this.client = new DocumentClient(new Uri(endpoint), primarykey);
}

Lo que en el constructor es darle un nombre a la base de datos cosmos y a la colección un nombre. Con endpoint y primary almacenamos la URI y la CLAVE PRINCIPAL en sus respectivas variables. Por último lo que hace es crear el cliente con el que manejaremos Cosmos DB.

Los siguientes métodos que haremos serán los de crear la base de datos y la colección. Su código es el siguiente:

Crear base de datos:

public async Task CrearBBDD()
{
       Database bbdd = new Database() { Id = this.bbdd };
       await this.client.CreateDatabaseAsync(bbdd);
}

Crear colección de películas:

public async Task CrearColeccionPeliculas()
{
       DocumentCollection collection = new DocumentCollection() { Id = this.collection };
//Crea la URI de la base de datos para implementar la colleción
await this.client.CreateDocumentCollectionAsync(UriFactory.CreateDatabaseUri(this.bbdd), collection);
}

Los siguiente métodos que crearemos serán los de añadir, modificar y eliminar peliculas.

Insertar película:

public async Task InsertarPelicula(Pelicula peli)
{
       await this.client.CreateDocumentAsync(UriFactory.CreateDocumentCollectionUri(this.bbdd, this.collection), peli);
}

Modificar película:

public async Task ModificarPelicula(Pelicula peli)
{
      Uri uri = UriFactory.CreateDocumentUri(this.bbdd, this.collection, peli.Id);
      await this.client.ReplaceDocumentAsync(uri, peli);
}

Eliminar película:

public async Task EliminarPelicula(String idPelicula)
{
       Uri uri = UriFactory.CreateDocumentUri(this.bbdd, this.collection, idPelicula);
       await this.client.DeleteDocumentAsync(uri);
}

Ahora lo que haremos es crear los métodos de búsqueda que serán los de obtener todas las películas, obtener las pelícuas por un género y obtener una pelicula por su ID.

Obtener películas:

public List<Pelicula> GetPeliculas()
{
       //Indicamos el numero de documentos a recuperar
       FeedOptions options = new FeedOptions() { MaxItemCount = -1 }; 
       //-1 te devuelven todos

       //Filtro de la coleccion
       String sql = "SELECT * FROM c";

       Uri uri = UriFactory.CreateDocumentCollectionUri(this.bbdd, this.collection);
       IQueryable<Pelicula> consulta = this.client.CreateDocumentQuery<Pelicula>(uri, sql, options);
       return consulta.ToList();
}

Obtener películas por género:

public List<Pelicula> BuscarPeliculasGenero(String genero)
{
       //Indicamos que nos devuelvan todos los datos
       FeedOptions queryOptions = new FeedOptions { MaxItemCount = -1 };

       IQueryable<Pelicula> query = this.client.CreateDocumentQuery<Pelicula>( UriFactory.CreateDocumentCollectionUri(this.bbdd, this.collection), queryOptions).Where(x => x.Genero == genero);
       return query.ToList();
}

Obtener película por Id:

public async Task<Pelicula> BuscarPeliculaAsync(String idPelicula)
{
       Document document = await this.client.ReadDocumentAsync(UriFactory.CreateDocumentUri(this.bbdd, this.collection, idPelicula));
       MemoryStream memory = new MemoryStream();
       using (var stream = new StreamReader(memory))
       {
              document.SaveTo(memory);
              memory.Position = 0;
              Pelicula pelicula = JsonConvert.DeserializeObject<Pelicula>(await stream.ReadToEndAsync());
       return pelicula;
            }
        }

A continuación he hecho un método para meter las películas a mano en vez de sacarlas de una base de datos.

public List<Pelicula> CrearPeliculas()
        {
            List<Pelicula> peliculas = new List<Pelicula>();
            Pelicula peli = new Pelicula
            {
                Id = "1",
                Titulo = "Regreso al futuro",
                TituloOriginal = "Back to the future",
                Genero = "Ciencia Ficcion",
                Sinopsis = "Una máquina del tiempo transporta a un adolescente a los años 50, cuando sus padres todavía estudiaban en la secundaria.",
                Imagen = "http://es.web.img3.acsta.net/c_215_290/pictures/14/03/11/10/30/351336.jpg"
            };
            peliculas.Add(peli);

            Pelicula peli2 = new Pelicula
            {
                Id = "2",
                Titulo = "Spider-man: Un nuevo universo",
                TituloOriginal = "Spider-man: into the spider-Verse",
                Genero = "Animacion",
                Sinopsis = "Luego de ser mordido por una araña radioactiva, el joven Miles Morales desarrolla misteriosos poderes que lo transforman en el Hombre Araña. Ahora deberá usar sus nuevas habilidades ante el malvado Kingpin, un enorme demente que puede abrir portales hacia otros universos.",
                Imagen = "http://t0.gstatic.com/images?q=tbn:ANd9GcQyfxXHWbpUJOUSgnBv2AyF0uL1Br9P3b_Erf1uB_gG8yZHUiv-"
            };
            peliculas.Add(peli2);

            Pelicula peli3 = new Pelicula
            {
                Id = "3",
                Titulo = "Origen",
                TituloOriginal = "Inception",
                Genero = "Ciencia Ficcion",
                Sinopsis = "Dom Cobb es un ladrón con una extraña habilidad para entrar a los sueños de la gente y robarles los secretos de sus subconscientes. Su habilidad lo ha vuelto muy popular en el mundo del espionaje corporativo, pero ha tenido un gran costo en la gente que ama. Cobb obtiene la oportunidad de redimirse cuando recibe una tarea imposible: plantar una idea en la mente de una persona. Si tiene éxito, será el crimen perfecto, pero un enemigo se anticipa a sus movimientos.",
                Imagen = "http://es.web.img3.acsta.net/c_215_290/medias/nmedia/18/72/41/74/20198901.jpg"
            };
            peliculas.Add(peli3);

            Pelicula peli4 = new Pelicula
            {
                Id = "4",
                Titulo = "Pesadilla antes de Navidad",
                TituloOriginal = "Nightmare Before Christmas",
                Genero = "Animacion",
                Sinopsis = "El rey de las calabazas en el pueblo de las brujas planea secuestrar a Santa Claus y al mismo tiempo llevar pánico en vez de alegría.",
                Imagen = "https://pics.filmaffinity.com/the_nightmare_before_christmas-136057718-large.jpg"
            };
            peliculas.Add(peli4);

            Pelicula peli5 = new Pelicula
            {
                Id = "5",
                Titulo = "Arrietty y el mundo de los diminutos",
                TituloOriginal = "Karigurashi no Arriety",
                Genero = "Animacion",
                Sinopsis = "Arrietty, una pequeña joven, vive con sus padres en una casa de los suburbios, a escondidas del dueño y la ama de casa. Como otros de su especie, Arrietty se mantiene oculta de sus anfitriones humanos, pero ocasionalmente se aventura más allá de los pisos en busca de azúcar y otros suministros. Una amistad secreta se forma cuando Shawn, de 12 años, conoce a Arrietty, pero su relación podría ser peligrosa para la familia de Arrietty.",
                Imagen = "http://es.web.img2.acsta.net/c_215_290/medias/nmedia/18/84/87/96/19769123.jpg"
            };
            peliculas.Add(peli5);

            return peliculas;
        }

PeliculasController

Una vez hecho el repository procedemos a crear el controlador. Para ello vamos a la carpeta Controllers, damos click derecho sobre la carpeta, seleccionamos la opción «Add…» y elegimos la opción «Controller«. Elegimos «MVC Controller – Empty«. Llamamos al controlador PeliculasController.

Dentro de PeliculasController declaramos la siguiente variable y hacemos una inyección en el constructor del controlador.

RepositoryCosmosDb repo;

public PeliculasController(RepositoryCosmosDb repo)
{
       this.repo = repo;
}

Recuerda hacer el using con ALT + ENTER si sale el subrayado rojo.

Index

A continuación dentro de PeliculasController creamos un IActionResult que se llamará Index.

public IActionResult Index()
{   
       return View();
}

Luego hacemos su método POST en el que le enviamos una cadena para darle las ordenes de crear la base de datos, las colección e insertar las peliculas en la colección.

[HttpPost]
public async Task<IActionResult> Index(String accion)
{
       if(accion == "CREAR")
       {
              await this.repo.CrearBBDD();
              ViewData["MENSAJE"] = "Base de datos creada";
              await this.repo.CrearColeccionPeliculas();
              ViewData["MENSAJE1"] = "Coleccion creada";
              List<Pelicula> peliculas = this.repo.CrearPeliculas();
              foreach (Pelicula peli in peliculas)
              {
                     await this.repo.InsertarPelicula(peli);
              }
              ViewData["MENSAJE2"] = "Peliculas insertadas";
      }     
      return View();
}

Nuestra siguiente acción será dar click derecho sobre index, elegir la opción «Add View…» y le damos al botón «Add«. Con esto crearemos la vista.

En la vista creamos un formulario con un botón con el nombre «accion» y el valor «CREAR» para que cuando se envie el formulario de la orden de crear.

@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>

<form method="post">
    <button name="accion" value="CREAR" class="btn btn-success">CREAR BBDD y Colection</button>
</form>


<h3>@Html.ActionLink("Ver Peliculas", "VerPeliculas")</h3>


<h3 style="color: green">@ViewData["MENSAJE"]</h3>
<h3 style="color: green">@ViewData["MENSAJE1"]</h3>
<h3 style="color: green">@ViewData["MENSAJE2"]</h3>

Como podemos observar, la vista tiene un enlace al que irá al IActionResult VerPeliculas por lo que procedemos a crearlo.

VerPeliculas

En PeliculasController creamos un IActionResult que se llamará VerPeliculas que devolverá una lista de películas.

public IActionResult VerPeliculas()
{
       List<Pelicula> peliculas = this.repo.GetPeliculas();
       return View(peliculas);
}

Vamos a hacer que también recoja por POST el género y que haga el filtro de las películas por género.

[HttpPost]
public IActionResult VerPeliculas(String genero)
{
       List<Pelicula> peliculas = this.repo.BuscarPeliculasGenero(genero);
       return View(peliculas);
}

Procedemos a crear la vista también.

@using ProyectoCosmosDb.Models
@model List<Pelicula>

@{
    ViewData["Title"] = "Ver Peliculas";
}

<h2>Peliculas</h2>

<p>
    <a asp-action="CrearPelicula">Crear nueva pelicula</a>
</p>

<form method="post">
    <input type="text" placeholder="Buscar por genero" name="genero" /> 
    <button type="submit" class="btn btn-success">Buscar</button>
</form>
@if(Model.Count() > 0)
{
<table class="table">
    <thead>
        <tr>
            <th>
                Id
            </th>
            <th>
                Titulo
            </th>
            <th>
                TituloOriginal
            </th>
            <th>
                Sinopsis
            </th>
            <th>
                Genero
            </th>
            <th>
                Imagen
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (Pelicula p in Model)
        {
            <tr>
                <td>@p.Id</td>
                <td>@p.Titulo</td>
                <td>@p.TituloOriginal</td>
                <td style="width:500px;"><p>@p.Sinopsis</p></td>
                <td>@p.Genero</td>
                <td><img src=" @p.Imagen" style="width:130px; height:200px;" /></td>
                <td>
                    <p>@Html.ActionLink("Editar", "EditPelicula", new { idPelicula = p.Id })</p>  
                   <p>@Html.ActionLink("Detalles", "DetallesPelicula", new { idPelicula = p.Id })</p> 
                   <p>@Html.ActionLink("Eliminar", "EliminarPelicula", new { idPelicula = p.Id })</p> 
                </td>
            </tr>
        }
    </tbody>
</table>
}

DetallesPelicula

En PeliculasController creamos un IActionResult que se llamará DetallesPelicula. En este IActionResult obtendrá los detalles de la película por el ID.

public async Task<IActionResult> DetallesPelicula(String idPelicula)
{
       Pelicula pelicula = await this.repo.BuscarPeliculaAsync(idPelicula);
       return View(pelicula);
}

La vista debe de quedar de esta manera:

@using ProyectoCosmosDb.Models
@model Pelicula

@{
    ViewData["Title"] = "DetallesPeliculas";
}

<h2>Detalles Pelicula</h2>

<div>
    <h4>Pelicula</h4>
    <hr />
    <dl class="dl-horizontal">
        <dt>
            Id
        </dt>
        <dd>
            @Model.Id
        </dd>
        <dt>
            Titulo
        </dt>
        <dd>
            @Model.Titulo
        </dd>
        <dt>
            Titulo Original
        </dt>
        <dd>
            @Model.TituloOriginal
        </dd>
        <dt>
            Sinopsis
        </dt>
        <dd>
            @Model.Sinopsis
        </dd>
        <dt>
            Genero
        </dt>
        <dd>
            @Model.Genero
        </dd>
        <dt>
            Imagen
        </dt>
        <dd>
            <img src="@Model.Imagen" style="width:130px; height:200px" />
        </dd>
    </dl>
</div>
<div>
    @Html.ActionLink("Editar", "EditPelicula", new {  idPelicula = Model.Id  }) |
    <a asp-action="VerPeliculas">Back to List</a>
</div>

CrearPelicula

Creamos un IActionResult que se llamará CrearPelicula que recogerá pos POST una pelicula que creemos, así queda el siguiente código:

public IActionResult CrearPelicula()
{
       return View();
}

[HttpPost]
public async Task<IActionResult> CrearPelicula(Pelicula pelicula)
{
       await this.repo.InsertarPelicula(pelicula);
       return View();
}

La vista de CrearPelicula es la siguiente:

@model ProyectoCosmosDb.Models.Pelicula

@{
    ViewData["Title"] = "CrearPelicula";
}

<h2>CrearPelicula</h2>

<h4>Pelicula</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="CrearPelicula">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Id" class="control-label"></label>
                <input asp-for="Id" class="form-control" />
                <span asp-validation-for="Id" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Titulo" class="control-label"></label>
                <input asp-for="Titulo" class="form-control" />
                <span asp-validation-for="Titulo" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="TituloOriginal" class="control-label"></label>
                <input asp-for="TituloOriginal" class="form-control" />
                <span asp-validation-for="TituloOriginal" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Sinopsis" class="control-label"></label>
                <input asp-for="Sinopsis" class="form-control" />
                <span asp-validation-for="Sinopsis" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Genero" class="control-label"></label>
                <input asp-for="Genero" class="form-control" />
                <span asp-validation-for="Genero" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Imagen" class="control-label"></label>
                <input asp-for="Imagen" class="form-control" />
                <span asp-validation-for="Imagen" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

EditPelicula

Creamos un IActionResult llamado EditPelicula en el que recogerá el id de la película y recogerá por POST el objeto pelicula que modificaremos.

public async Task<IActionResult> EditPelicula(String idPelicula)
{
       Pelicula pelicula = await this.repo.BuscarPeliculaAsync(idPelicula);
       return View(pelicula);
}

[HttpPost]
public async Task<IActionResult> EditPelicula(Pelicula pelicula)
{
       await this.repo.ModificarPelicula(pelicula);
       return RedirectToAction("VerPeliculas");
}

La vista es la siguiente:

@model ProyectoCosmosDb.Models.Pelicula

@{
    ViewData["Title"] = "EditPelicula";
}

<h2>EditPelicula</h2>

<h4>Pelicula</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="EditPelicula">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group" hidden>
                <label asp-for="Id" hidden class="control-label"></label>
                <input asp-for="Id" hidden class="form-control" />
                <span asp-validation-for="Id" hidden class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Titulo" class="control-label"></label>
                <input asp-for="Titulo" class="form-control" />
                <span asp-validation-for="Titulo" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="TituloOriginal" class="control-label"></label>
                <input asp-for="TituloOriginal" class="form-control" />
                <span asp-validation-for="TituloOriginal" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Sinopsis" class="control-label"></label>
                <input asp-for="Sinopsis" class="form-control" />
                <span asp-validation-for="Sinopsis" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Genero" class="control-label"></label>
                <input asp-for="Genero" class="form-control" />
                <span asp-validation-for="Genero" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Imagen" class="control-label"></label>
                <input asp-for="Imagen" class="form-control" />
                <span asp-validation-for="Imagen" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Save" class="btn btn-default" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-action="VerPeliculas">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

EliminarPelicula

Este IActionResult es el único que no tiene una vista ya que solo hará la acción de eliminar. Su código es el siguiente:

 public async Task<IActionResult> EliminarPelicula(String idPelicula)
{
       await this.repo.EliminarPelicula(idPelicula);
       return RedirectToAction("VerPeliculas");
}

Con esto ya hemos terminado de hacer el controlador de peliculas. Lo siguiente que haremos es dar el servicio de la inyección del RepositoryCosmosDb y establecer como página principal el index de PeliculasController.

Startup.cs

Nos dirigimos al archivo Startup.cs y en el método ConfigureServices añadimos la siguiente línea:

services.AddTransient<RepositoryCosmosDb>();

Luego en el método Configure modificamos el app.UseMvc por el siguiente código:

 app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Peliculas}/{action=Index}/{id?}");
            });

Con esto ya tendríamos todo listo. Lo único que queda ahora es interactuar con la aplicación, creando la base de datos primero para que funcione.

Autor/a: David Valencia Beltrán

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

Centro: Tajamar

Año académico: 2018-2019

Código / recursos utilizados / Otros datos de interés: https://github.com/Davos95/ProyectoCosmosDb

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.