En este post vamos a aprender a crear una aplicación .NET Core MVC que será capaz de:

  • Añadir registros a MongoDb
  • Eliminar registros de MongoDb
  • Editar registros MongoDb

Para comenzar primero debemos crear la base de datos, tenemos dos opciones, descargar la base de datos en local o usar MongoDb Atlas en la nube (es gratuito), yo voy a utilizar Atlas

Creación de base de datos en MongoDb Atlas

Accedemos a la página oficial de MongoDb Atlas:

Elegimos Try Free para crear una cuenta gratuita y nos registramos

Una vez que nos hayamos registrado, accedemos a nuestra cuenta de Atlas y veremos una web similar a esta:

Ahora tenemos que crear una base de datos, para ello elegimos Build a Database

Ahora seleccionamos el plan gratuito

Ahora podemos cambiar la región o el proveedor cloud donde estará alojada nuestra base de datos, e mi caso lo voy a dejar por defecto

Ahora tenemos que crear un usuario para poder acceder a la base de datos:

También debemos añadir las IP desde las que se va a poder acceder a la base de datos, si queremos que cualquier IP se pueda conectar a nuestra base de datos pondremos la dirección 0.0.0.0, en mi caso voy a añadir la IP específica de mi equipo.

Cuando hayamos creado el usuario y añadido la dirección IP a la whitelist finalizamos la configuración

Ahora comprobamos que el cluster se ha creado correctamente

Obtener Connection String

Ahora debemos obtener la cadena de conexión para poder conectarnos desde nuestro proyecto, para ello seleccionamos connect en nuestro cluster

Ahora elegimos el tipo de conexión que queremos, en este caso queremos conectarlo con una aplicación

Por último debemos elegir para que tipo de aplicación queremos la cadena de conexión, en este caso será C#/.NET versión 5

Hay que tener en cuenta que en la cadena de conexión no aparecerá nuestra contraseña, debemos sustituir <password> por nuestra contraseña.

Creación del proyecto y dependencias

Vamos a crear el proyecto Net Core MVC de tipo ASP.NET Core WebApp(Model-View-Controller)

Ahora ponemos un nombre al proyecto, en mi caso lo voy a llamar MvcMongoCrud, también seleccionamos la versión de NET Core que queremos en mi caso la versión 5

Ahora debemos añadir el NuGet de Mongo para poder trabajar con la base de datos, para ello, sobre el proyecto hacemos click derecho y seleccionamos Manage NuGet Packages

Ahora en la sección de Browse, buscamos MongoDB.Driver e instalamos el NuGet

Ahora vamos a almacenar la cadena de conexión en nuestro proyecto, para ello abrimos el archivo appsettings.json y en la sección de ConnectionStrings añadimos la cadena de conexión que hemos obtenido anteriormente:

appsettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "cadenamongodb": "mongodb+srv://diego:mongoadmin@cluster0.ygkfa.mongodb.net/myFirstDatabase?retryWrites=true&w=majority"
  }
}

Creación de modelo para almacenar en la base de datos

En mi caso voy a almacenar en la base de datos productos, un producto tendrá las siguientes características:

  • _id : id del objeto en la base de datos
  • precio : precio del producto
  • imagen : imagen del producto
  • descripción : descripción del producto
  • nombre : nombre del producto
  • fecha : fecha de creación del producto

Tenemos que crear la clase producto, para ello, sobre la carpeta Models damos click derecho y seleccionamos Add/Class..

Le ponemos un nombre a la clase, en mi caso será Producto

Ahora tenemos que crear la clase con los atributos que hemos mencionado anteriormente, además debemos mapear los elementos de la clase usando las anotaciones de Bson que es el «lenguaje» que usa MongoDb, utilizaremos 2:

  • [BsonId] : Representa el id del objeto en la base de datos
  • [BsonElement(«nombre en la base de datos»)] : Representa una propiedad del objeto en la base de datos
Producto.cs

using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace MvcMongoCrud.Models
{
    public class Producto
    {
        [BsonId]
        public ObjectId _id { get; set; }
        [BsonElement("precio")]
        public int precio { get; set; }
        [BsonElement("imagen")]
        public String imagen { get; set; }
        [BsonElement("descripcion")]
        public String descripcion { get; set; }
        [BsonElement("nombre")]
        public String nombre { get; set; }
        [BsonElement("fecha")]
        public DateTime fecha { get; set; }

    }
}

Creación del Context de la base de datos

El Context nos permitirá inyectar la base de datos en otras clases, es decir nos permitirá usar la base de datos en otras clases, para crearlo, sobre el proyecto hacemos click derecho y seleccionamos Add/Folder, la llamaremos Data

Dentro de data crearemos una clase llamada ProductosContext, para ello hacemos click derecho en Data y seleccionamos Add/Class..

Para que esta clase funcione correctamente debemos inyectar la Interfaz IConfiguration, para poder acceder a la configuración y recuperar la cadena de conexión que hemos almacenado previamente, además debemos crear un MongoClient que nos permitirá acceder a nuestro Cluster.

ProductosContext.cs

using Microsoft.Extensions.Configuration;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace MvcMongoCrud.Data
{
    public class ProductosContext
    {
        public MongoClient client;

        public ProductosContext(IConfiguration configuration)
        {
            this.client = new MongoClient(configuration.GetConnectionString("cadenamongodb"));
        }

    }
}

Creación del Repository de Productos

El Repository nos permite crear métodos para por ejemplo obtener todos los Productos de la base de datos, en este proyecto tenemos los siguientes métodos:

  • getProductos() : Obtiene todos los Productos de la base de datos
  • findProducto(String id) : Obtiene el producto asociado al id especificado
  • UpdateProducto(Producto p) : Actualiza el producto que le digamos
  • DeleteProducto(String id): Elimina el producto asociado al id especificado
  • InsertProducto(String nombre, String imagen, String descripcion, int precio): Almacena un producto almacenado en la base de datos

Para crear el Repository debemos crear una carpeta llamada Repositories, igual que creamos la carpeta Data del Context, después de crear la carpeta debemos de crear la clase RepositoryProductos

En el Repository de productos debemos inyectar el Context que hemos creado antes, para ello creamos una instancia del Context y lo inyectamos en el constructor

 private ProductosContext context;

        public RepositoryProductos(ProductosContext context)
        {
            this.context = context;
        }

Ahora crearemos el método getProductos() que nos devolverá una lista con todos los productos de la base de datos:

 public List<Producto> getProductos()
        {
            var consulta = this.context.client.GetDatabase("Productos").GetCollection<Producto>("Productos").AsQueryable();

            return consulta.ToList();
        }

Ahora crearemos el método findProducto(String id) que nos devolverá el objeto producto asociado a la id que le pasemos por parámetro

 public Producto findProducto(String id)
        {
            Producto p = this.context.client.GetDatabase("Productos").GetCollection<Producto>("Productos").Find(new BsonDocument { {"_id", new ObjectId(id) } }).FirstOrDefault();

            return p;

        }

Ahora crearemos el método UpdateProducto(Producto p) que modificará el producto que le pasemos por parámetro en la base de datos

 public void UpdateProducto(Producto p)
        {
            var filter = Builders<Producto>.Filter.Eq(x => x._id, p._id);

            this.context.client.GetDatabase("Productos").GetCollection<Producto>("Productos").ReplaceOne(filter, p);
        }

También crearemos el método DeleteProducto(String id) que eliminará de la base de datos el producto asociado a la id que le pasemos por parámetro

 public void DeleteProducto(String id)
        {
           
            var filter = Builders<Producto>.Filter.Eq(x => x._id, new ObjectId(id));
            this.context.client.GetDatabase("Productos").GetCollection<Producto>("Productos").DeleteOne(filter);
        } 

Por último crearemos el método InsertProducto(String nombre, String imagen, String descripcion, int precio) que creará y guardará en la base de datos un Producto con las características que le hemos especificado

 Producto p = new Producto();
            p.nombre = nombre;
            p.descripcion = descripcion;
            p.imagen = imagen;
            p.precio = precio;
            p._id = ObjectId.GenerateNewId();
            p.fecha = DateTime.UtcNow;

            this.context.client.GetDatabase("Productos").GetCollection<Producto>("Productos").InsertOne(p);

Clase competa:

using MongoDB.Bson;
using MongoDB.Driver;
using MvcMongoCrud.Data;
using MvcMongoCrud.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace MvcMongoCrud.Repositories
{
    public class RepositoryProductos
    {
        private ProductosContext context;

        public RepositoryProductos(ProductosContext context)
        {
            this.context = context;
        }

        public List<Producto> getProductos()
        {
            var consulta = this.context.client.GetDatabase("Productos").GetCollection<Producto>("Productos").AsQueryable();

            return consulta.ToList();
        }


        public Producto findProducto(String id)
        {
            Producto p = this.context.client.GetDatabase("Productos").GetCollection<Producto>("Productos").Find(new BsonDocument { {"_id", new ObjectId(id) } }).FirstOrDefault();

            return p;

        }

        public void UpdateProducto(Producto p)
        {
            var filter = Builders<Producto>.Filter.Eq(x => x._id, p._id);

            this.context.client.GetDatabase("Productos").GetCollection<Producto>("Productos").ReplaceOne(filter, p);
        }

        public void DeleteProducto(String id)
        {
           
            var filter = Builders<Producto>.Filter.Eq(x => x._id, new ObjectId(id));
            this.context.client.GetDatabase("Productos").GetCollection<Producto>("Productos").DeleteOne(filter);
        } 

        public void InsertProducto(String nombre, String imagen, String descripcion, int precio)
        {
            Producto p = new Producto();
            p.nombre = nombre;
            p.descripcion = descripcion;
            p.imagen = imagen;
            p.precio = precio;
            p._id = ObjectId.GenerateNewId();
            p.fecha = DateTime.UtcNow;

            this.context.client.GetDatabase("Productos").GetCollection<Producto>("Productos").InsertOne(p);
        }
    }
}

Preparación del Controller de la aplicación

En este caso, vamos a usar el HomeController que viene por defecto en la aplicación, para que todo funcione correctamente, debemos inyectar en el constructor el RepositoryProductos, que es la clase que contiene los métodos que manejan la base de datos

 private RepositoryProductos repo;
        public HomeController(RepositoryProductos repo)
        {
            this.repo = repo;
        }

Mostrar todos los productos de la base de datos

Para mostrar todos los productos de la base de datos, cargaremos los productos cuando se llame a la acción Index del HomeController, a su vez llamaremos al método getProductos() de nuestro RepositoryProductos

public IActionResult Index()
        {
            List<Producto> productos = this.repo.getProductos();

            if (productos.Count == 0)
            {
                ViewBag.mensaje = "No hay productos en la base de datos";
                return View();
            }
            else
            {
                return View(productos);
            }

            
        }

También modificamos la vista Index, que está dentro de la carpeta Views/Home/Index.cshtml

Index.cshtml

@model List<Producto>

<h3>Lista de productos:</h3>
<br />
<a class="btn btn-primary" asp-controller="Home" asp-action="InsertarProducto">Insertar un producto</a>
<br />

@if (Model != null)
{
    <table class="table table-striped table-hover">

        <thead>
        <th>Nombre</th>
        <th>Descripcion</th>
        <th>Precio</th>
        <th>Imagen</th>
        <th>Editar</th>
        <th>Eliminar</th>
        <tbody>
            @foreach (Producto p in @Model)
            {
            <tr>
                <td>@p.nombre</td>
                <td>@p.descripcion</td>
                <td>@p.precio €</td>
                <td><img src="@p.imagen" style="width:150px; height:150px" /></td>
                <td><a class="btn btn-outline-warning" asp-controller="Home" asp-action="Editar" asp-route-id="@p._id.ToString()">Editar</a></td>
                <td><a class="btn btn-outline-danger" asp-controller="Home" asp-action="Eliminar" asp-route-id="@p._id">Eliminar</a></td>

            </tr>
            }

        </tbody>


    </table>
}else
            {
                <h4>@ViewBag.mensaje</h4>
            }

<br/>

Insertar un producto en la base de datos

Para insertar un producto en la base de datos debemos crear dos métodos en el HomeController, el primero nos devolverá la vista del formulario que nos permitirá rellenar los datos y el segundo nos permitirá recibir los datos del formulario y mandarlos a la base de datos.

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

Ahora debemos crear la vista del formulario que nos permitirá rellenar los datos del producto, para ello dentro de Views/Home crearemos una vista llamada InsertarProducto.cshtml

InsertatProducto.cshtml

<h1 class="text-muted">Insertar Un Producto</h1>

<form method="post">

    <label>Nombre</label>
    <input class="form-control" name="nombre" type="text" required />
    <label>Descripcion</label>
    <input class="form-control" name="descripcion" type="text" required />
    <label>Precio</label>
    <input class="form-control" name="precio" type="number" required />
    <label>Imagen</label>
    <input class="form-control" name="imagen" type="text" required />
    <br/>
    <button class="btn btn-secondary">Guardar producto</button>

</form>

Ahora crearemos en HomeController el método InsertarProducto(String nombre, String imagen, String descripcion, int precio) que creará y guardará en la base de datos un Producto con las características que le hemos, pero debemos tener en cuenta que le debemos indicar que a este método se accederá por post, ya que se accederá cuando se mande el formulario.

Dentro de este método llamaremos a nuestro RepositoryProductos, al método InsertProducto, después devolveremos una redirección a Index para que después de insertar se nos redirija a la lista con todos los productos

[HttpPost]
public IActionResult InsertarProducto(String nombre, String descripcion, int precio, String imagen)
        {
            this.repo.InsertProducto(nombre, imagen, descripcion, precio);
            return RedirectToAction("Index");
        }

Editar un producto

Para editar un producto debemos enviar el producto editado al método UpdateProducto de nuestro RepositoryProductos, en el Index.cshtm existe un botón que manda el id del producto al método Editar de HomeController, devolveremos el producto con ese id a la vista para poder rellenar el formulario de edición

 public IActionResult Editar(String id)
        {
            Producto p = this.repo.findProducto(id);
            return View(p);
        }

También debemos crear una vista en Views/Home llamada Editar.cshtml, que contendrá el formulario de edición del producto

Editar.cshtml

@model Producto



<h1 class="text-muted">Editar Producto</h1>

<form method="post">
    <input class="form-control" hidden name="id" type="text" value="@Model._id" required />
    <label>Nombre</label>
    <input class="form-control" name="nombre" type="text" value="@Model.nombre" required />
    <label>Descripcion</label>
    <input class="form-control" name="descripcion" type="text" value="@Model.descripcion" required />
    <label>Precio</label>
    <input class="form-control" name="precio" type="number" value="@Model.precio" required />
    <label>Imagen</label>
    <input class="form-control" name="imagen" type="text" value="@Model.imagen" required />
    <br />
    <button class="btn btn-secondary">Actualizar producto</button>

</form>

Cuando el usuario termine de editar el producto debemos recoger los nuevos datos del producto y modificarlo en la base de datos, para ello en el HomeController crearemos otro método Editar que recibirá los datos del producto editado y lo modificará en la base de datos llamando al método UpdateProduct de nuestro RepositoryProductos, a este método también se accederá mediante POST ya que debemos recoger la información del formulario

[HttpPost]
        public IActionResult Editar(String id, String nombre, String descripcion, int precio, String imagen)
        {
            Producto p = this.repo.findProducto(id);
            p.nombre = nombre;
            p.descripcion = descripcion;
            p.precio = precio;
            p.imagen = imagen;

            this.repo.UpdateProducto(p);

            return RedirectToAction("Index");
        }

Este método devolverá una redirección a Index para poder ver todos los productos, con el producto editado.

Eliminar un producto

Para eliminar un producto crearemos en HomeController un método llamado Eliminar, le pasaremos el id del producto que queremos eliminar, para ello llamaremos al método DeleteProducto de nuestro RepositoryProductos

public void DeleteProducto(String id)
        {
           
            var filter = Builders<Producto>.Filter.Eq(x => x._id, new ObjectId(id));
            this.context.client.GetDatabase("Productos").GetCollection<Producto>("Productos").DeleteOne(filter);
        } 

Para llamar a este método, en el Index, el botón de eliminar guarda el id del producto al que pertenece y se lo manda al método Eliminar del HomeController

HomeController completo

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using MvcMongoCrud.Models;
using MvcMongoCrud.Repositories;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;

namespace MvcMongoCrud.Controllers
{
    public class HomeController : Controller
    {
        private RepositoryProductos repo;
        public HomeController(RepositoryProductos repo)
        {
            this.repo = repo;
        }

        public IActionResult Index()
        {
            List<Producto> productos = this.repo.getProductos();

            if (productos.Count == 0)
            {
                ViewBag.mensaje = "No hay productos en la base de datos";
                return View();
            }
            else
            {
                return View(productos);
            }

            
        }


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

        [HttpPost]
        public IActionResult InsertarProducto(String nombre, String descripcion, int precio, String imagen)
        {
            this.repo.InsertProducto(nombre, imagen, descripcion, precio);
            return RedirectToAction("Index");
        }

        public IActionResult Editar(String id)
        {
            Producto p = this.repo.findProducto(id);
            return View(p);
        }

        [HttpPost]
        public IActionResult Editar(String id, String nombre, String descripcion, int precio, String imagen)
        {
            Producto p = this.repo.findProducto(id);
            p.nombre = nombre;
            p.descripcion = descripcion;
            p.precio = precio;
            p.imagen = imagen;

            this.repo.UpdateProducto(p);

            return RedirectToAction("Index");
        }

        public IActionResult Eliminar(String id)
        {
            this.repo.DeleteProducto(id);

            return RedirectToAction("Index");
        }

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

       
    }
}

Resolución de dependencias en StartUp.cs

Finalmente debemos añadir las dependencias en StartUp.cs para que puedan ser inyectadas en las diferentes clases, para ello abrimos StartUp.cs y añadimos las siguientes lineas

public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<ProductosContext>();
            services.AddTransient<RepositoryProductos>();
            services.AddControllersWithViews();
        }

StartUp.cs completo:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using MvcMongoCrud.Data;
using MvcMongoCrud.Repositories;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace MvcMongoCrud
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<ProductosContext>();
            services.AddTransient<RepositoryProductos>();
            services.AddControllersWithViews();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }
            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

Finalmente ya podremos ejecutar el programa y comprobar que todo funciona, debe quedarnos de esta manera:

También comprobaremos que en la base de datos aparecen los productos

Autor/a: Diego Plaza Rodán

Curso: Desarrollo Web Full Stack, MultiCloud y Multiplataforma

Centro: Tajamar

Año académico: 2021-2022

GitHub: https://github.com/plaza19/MvcMongoCrud.git

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.