Para el desarrollo de este proyecto utilizaremos las siguientes tecnologías y herramientas:
- Visual Studio 2019
- C# y .Net Core 5.0
- SQL Server
- Entity Framework (con la técnica Code First)
Entorno y dependencia de paquetes
Lo primero será crear un proyecto con Visual Studio 2019 (Puedes usar otra versión si lo deseas pero recomiendo que sea ésta) de tipo Aplicación web ASP.NET Core, la llamaremos APIBiblioteca y será de tipo Core Web API, como se muestra en las siguientes imágenes:
Ahora instalaremos los paquetes necesarios para que funcione el Entity Framework en un proyecto .Net Core y Newtonsoft para funcionalidades con serialización y deserialización de json, éstos son:
- Microsoft.EntityFrameworkCore.SqlServer
- Microsoft.EntityFrameworkCore.Tools
- Microsoft.AspNetCore.Mvc.NewtonsoftJson
Estas dependencias se agregan desde el Nugget como se muestra a continuación:
Instalamos de la siguiente manera:
Estructura y configuraciones
Una vez instalados todos los paquetes que necesitamos, es momento de crear la estructura de carpetas del proyecto, así que creamos la carpeta Entities, en donde irán todas las entidades que se corresponderán cada una con una tabla de la base de datos, recordemos que utilizaremos Entity Framework con la técnica Code first.
💡 La técnica Code First consiste en crear una BD por completo a partir de nuestras entidades
En la carpeta Entities creamos dos clases: Autor y Libro como sigue:
public class Autor
{
public int Id { get; set; }
[Required]
public string Nombre { get; set; }
public List< Libro> Libros { get; set; }
}
public class Libro
{
public int Id { get; set; }
[Required]
public string Titulo { get; set; }
[Required]
public int AutorId { get; set; }
public Autor Autor { get; set; }
}
Recuerda añadir también la siguiente referencia para que puedas usar la anotación Required:
using System.ComponentModel.DataAnnotations;
Creamos la carpeta Context y dentro, la clase ApplicationDBContext:
using APIBiblioteca.Entities;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace APIBiblioteca.Context
{
public class ApplicationDBContext:DbContext
{
public ApplicationDBContext(DbContextOptions< ApplicationDBContext> options) : base(options)
{
}
public DbSet< Autor> Autor { get; set; }
public DbSet< Libro> Libro { get; set; }
}
}
Ahora debemos configurar la clase Startup, la abrimos y actualizamos el método ConfigureServices añadiendo las líneas resaltadas:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext< ApplicationDBContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnectionString")));
services.AddControllers().AddNewtonsoftJson(options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "APIBiblioteca", Version = "v1" });
});
}
Crearemos ahora la cadena de conexión, nos dirigimos al appsettings.json y añadimos lo siguiente:
{
"ConnectionStrings": {
"DefaultConnectionString": "server=.;initial catalog=BibliotecaBD;user=sa;password=12345"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
💡Cambiar el password por el que tiene tu servidor de BD así como los demás datos de la cadena de conexión si fuese necesario
💡Hay una forma más segura de guardar la cadena de conexión, pero por el momento es suficiente, en futuros posts les mostraré opciones mas profesionales y seguras.
Es momento de aplicar Entity Framework, en .Net Core esto se hace por consola, así que nos dirigimos a:
Ejecutamos los siguientes comandos:
add-migration initial
Esto creará la carpeta Migrations y una clase en donde se alojarán todas las migraciones, es decir todas las "versiones" por así decirlo de nuestra BD conforme vayamos actualizando nuestro modelo a través de las entidades:
Ahora ejecutamos el siguiente comando, lo que hará será crear nuestras tablas en la BD incluyendo relaciones, según nuestras entidades
update-database
Checamos en el SQL Management Studio y genial, allí está! 🎉
Cada vez que hagamos un cambio en nuestras entidades y queramos reflejarlas en nuestra base de datos debemos ejecutar los comandos siguientes uno después del otro:
add-migration nombreQueElijas
update-database
Controllers
Vamos a crear nuestros dos controladores: AutorController y LibroController:
💡Si en tu carpeta Controllers ves uno llamado WeatherForecast, puedes eliminarlo, VS lo crea como ejemplo
Veamos como quedaría el controlador AutorController:
using APIBiblioteca.Context;
using APIBiblioteca.Entities;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace APIBiblioteca.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class AutorController : ControllerBase
{
private readonly ApplicationDBContext context;
public AutorController(ApplicationDBContext context)
{
this.context = context;
}
[HttpGet]
public ActionResult< IEnumerable< Autor>> Get()
{
return context.Autor.Include(x => x.Libros).ToList();
}
[HttpGet("{id}", Name = "ObtenerAutor")]
public ActionResult< Autor> Get(int id)
{
var autor = context.Autor.Include(x => x.Libros).FirstOrDefault(x => x.Id == id);
if (autor == null)
{
return NotFound();
}
return autor;
}
[HttpPost]
public ActionResult Post([FromBody] Autor autor)
{
context.Autor.Add(autor);
context.SaveChanges();
return new CreatedAtRouteResult("ObtenerAutor", new { id = autor.Id }, autor);
}
[HttpPut("{id}")]
public ActionResult Put([FromBody] Autor autor, int id)
{
if (id != autor.Id)
{
return BadRequest();
}
context.Entry(autor).State = EntityState.Modified;
context.SaveChanges();
return Ok();
}
[HttpDelete("{id}")]
public ActionResult< Autor> Delete(int id)
{
var autor = context.Autor.FirstOrDefault(x => x.Id == id);
if (autor == null)
{
return NotFound();
}
//tambien puede usarse context.Autor.Remove(autor);
context.Entry(autor).State = EntityState.Deleted;
context.SaveChanges();
return autor;
}
}
}
Y ahora LibroController:
using APIBiblioteca.Context;
using APIBiblioteca.Entities;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace APIBiblioteca.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class LibroController : ControllerBase
{
private readonly ApplicationDBContext context;
public LibroController(ApplicationDBContext context)
{
this.context = context;
}
[HttpGet]
public ActionResult< List< Libro>> Get()
{
var libros = context.Libro.Include(x => x.Autor).ToList();
return libros;
}
[HttpGet("{id}", Name = "ObtenerLibro")]
public ActionResult< Libro> Get(int id)
{
var libro = context.Libro.Include(x => x.Autor).FirstOrDefault(x => x.Id == id);
if (libro == null)
{
return NotFound();
}
return libro;
}
[HttpPost]
public ActionResult Post([FromBody] Libro libro)
{
context.Libro.Add(libro);
context.SaveChanges();
return new CreatedAtRouteResult("ObtenerLibro", new { id = libro.Id }, libro);
}
[HttpPut("{id}")]
public ActionResult Put([FromBody] Libro libro, int id)
{
if (id != libro.Id)
{
return BadRequest();
}
context.Entry(libro).State = EntityState.Modified;
context.SaveChanges();
return Ok();
}
[HttpDelete("{id}")]
public ActionResult< Libro> Delete(int id)
{
var libro = context.Libro.FirstOrDefault(x => x.Id == id);
if (libro == null)
{
return NotFound();
}
context.Libro.Remove(libro);
context.SaveChanges();
return libro;
}
}
}
Sólo para que la tengas clara, la estructura del proyecto quedaría así:
Pruebas
Para las pruebas usaremos POSTMAN, veamos: Primero ejecutamos el proyecto (F5 o Ctrl + F5) y luego vamos a crear un nuevo autor, es decir probaremos el método POST del controlador Autor:
Todo parece estar bien, revisemos la BD:
💡 Recuerda que para probar algunos métodos de un API como POST y PUT deberás enviarle un body en json, como se muestra resaltado en las imágenes.
Esto es genial, funciona! 😁 ahora probemos el método GET:
No podría ser mejor amigos, todo nos salió a la perfección. ✨ Sigamos con las pruebas, actualicemos el autor, vamos a agregarle el apellido, en otras palabras pondremos a prueba al método PUT:
Ahora vamos a poner a prueba el método DELETE, eliminándome de la lista de autores 😑
Excelente amigos, podemos decir misión cumplida? No aún no, tu tarea será probar el controlador Libros! Vamos anímate, la pelota está en tu cancha estimado dev 😉
El código fuente está en mi cuenta de Github, pero te recomiendo que lo codees tu mismo y si lo descargas sólo sea para comparar.
Si te ha servido este contenido, me ayudarías mucho a mi y sobretodo a la comunidad dev compartiéndolo, un saludo crack!
Hola, Te envio un gran saludo.
Estoy intentando realizar el ejercicio planteado en este post, pero al ejecutarlo, me aparece el siguiente error en el archivo startup.cs: IServiceCollection.AddRazorPages’
inside the call to ‘ConfigureServices(…)’ in the application startup code.
No se a que se debe el mensaje incluso corrí tu proyecto tal cual y me sigue apareciendo el mensaje, entonces no se si es mi visual studio.
Agradezco tus tutoriales, me están ayudando mucho a entender sobre el tema.
Hola Kristian, un gusto saludarte.
El error que refieres parece estar relacionado en cómo creas tu proyecto, recuerda que aquí utilicé VS 2019 y .NET 5, si tu tienes otra versión cambia un poco. Tienes que asegurarte que el proyecto sea de tipo .NET CORE WEB API, este es el repo: https://github.com/GeaSmart/BibliotecaAPI
Éxitos y recuerda que cometiendo errores y resolviéndolos es como se aprende.
Comparte este blog en tus redes, y sígueme en mis redes también, un abrazo. 🥳🙌
Muchas gracias por tu respuesta.
Podría decirse que la única diferencia es que lo hice en VS2022, pero me asegure que el framework fuera el .NET 5, todas las librerías fueran las 5.0.0 y el proyecto esta creado como .net core web api.
Estoy creando el proyecto nuevamente desde cero con la versión 2019 del VS, por lo que estaré molestandote jajaja, es broma.
Muchos saludos.