Crud MongoDb MVC
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