Creando un API RESTFul Completo con Net Core para un Sistema CRM desde cero (3 de 5)
Tercera entrega: Aprende a hacer una Relación uno a muchos entre dos entidades y a utilizar las propiedades de navegación! 😉

Hey devs! esta es la tercera de cinco entradas donde estaremos creando un API con arquitectura RESTFul completo para hacer el backend de un sistema de gestión de relación con los clientes o más conocido por sus siglas en inglés: CRM (Customer relationship management). 

Para que no te pierdas, aquí están todas las entradas de la serie:

En esta ocasión aprenderás a hacer una relación de uno a muchos entre dos entidades: Prospectos y contactos.

Los contactos son las veces que un agente visita o contacta a un prospecto, entonces si te fijas bien, un agente puede contactar una o mil veces a un prospecto hasta hacerlo cliente. Empezamos entonces 😉.

Actualizando Entidades

Creando entidad Contacto:

    public class Contacto
    {
        [Key]
        public int Id { get; set; }
        [Required]
        public DateTime Fecha { get; set; }
        [Required]
        [StringLength(25)]
        public string Medio { get; set; }
        [StringLength(250)]
        public string Descripcion { get; set; }
        public int ProspectoId { get; set; } //sirve de nexo con la entidad Prospecto

        //Propiedades de navegación
        public Prospecto Prospecto { get; set; } //es para poder navegar hasta los datos del prospecto
    }

Si no ponemos la anotación StringLength, al momento de migrar a la base de datos se haría con el tipo de datos nvarchar(MAX) lo cual sería un desperdicio de recursos. En este proyecto dejé la propiedad UrlPerfil de la clase entidad Prospecto sin esa anotación y fíjate su tipo de datos cómo quedó 👀

Nota además que añadí una propiedad al final del tipo Prospecto, esta es una propiedad de navegación, ya que esta entidad está relacionada con la entidad Prospecto, de esta forma le estoy diciendo a Entity Framework que estoy relacionando ambas tablas.

PRO TIP: Entity Framework Code first dice: Cuando dos entidades están relacionadas con una relación uno a muchos, la entidad desde la cual parte la relación (osea entidad origen) llevará una propiedad tipo List<> y la entidad destino, en este caso Contacto, llevará una propiedad del mismo tipo que la entidad origen. Esto lo notarás a continuación...

Ahora entonces toca actualizar la entidad Prospecto, añadiéndole la propiedad tipo List<Contacto>

    public class Prospecto
    {
        [Key]
        public int Id { get; set; }

        [Required]
        [StringLength(70, ErrorMessage = "El campo {0} no debe exceder de {1} caracteres.")]
        public string Nombre { get; set; }
        public string UrlPerfil { get; set; }

        //Propiedades de navegación
        public List<Contacto> Contactos { get; set; }
    }

Actualizar Contexto

Cada vez que añades una nueva clase entidad, como ahora con el caso de Contacto, debes actualizar el contexto, en este proyecto es la clase ApplicationDBContext:

    public class ApplicationDBContext : DbContext
    {
        public ApplicationDBContext(DbContextOptions options) : base(options)
        {

        }
        public DbSet<Agente> Agentes { get; set; }
        public DbSet<Prospecto> Prospectos { get; set; }
        public DbSet<Contacto> Contactos { get; set; }
    }

Aplicando migración

Como hemos actualizado nuestras entidades, eso significa nuevas tablas en la base de datos, además hemos incluído relaciones entre tablas, para que todo esto se vea reflejado en la base de datos, ejecutamos una nueva migración, y así debes ejecutar migraciones cada vez que quieras actualizar tu modelo físico de base de datos.

Para aplicar una nueva migración debes crear una migración, yo la llamaré Contacto, el comando es el siguiente:

add-migration Contacto

Ahora ejecutamos la migración con:

update-database

Si revisas tu base de datos, deberías tener 3 tablas y dos de ellas relacionadas, con una relación tipo uno a muchos.

Para ver esto mejor, créate un diagrama de base de datos en SSMS.

Si tienes problemas para crear un nuevo diagrama de base de datos en tu base de datos CRM, haz clic derecho a tu base de datos, anda a Propiedades > Archivos > Propietario y agrega al usuario sa.

Para crear un diagrama, clic derecho a Diagramas de base de datos > Crear nuevo, añade las tablas, excepto una tabla de metadatos creada por EF llamada _EFMigrationsHistory, ordena las tablas, elige una vista de tabla estándar para cada una y guarda el diagrama:

Bien hecho, nuestras tablas se han creado y relacionado bien! 😊

Añadiendo los DTO para Contacto

Agregamos por el momento dos DTO para la entidad nueva: ContactoDTO y ContactoCreacionDTO

ContactoDTO:

    public class ContactoDTO
    {
        public int Id { get; set; }
        public DateTime Fecha { get; set; }
        public string Medio { get; set; }
        public string Descripcion { get; set; }
    }

ContactoCreacionDTO:

    public class ContactoCreacionDTO
    {
        [Required]
        public DateTime Fecha { get; set; }
        [Required]
        [StringLength(25)]
        public string Medio { get; set; }
        [StringLength(250)]
        public string Descripcion { get; set; }
    }

Como notarás no estoy incluyendo la propiedad ProspectoId, y es porque ésta irá incluída en la ruta del controlador ContactosController como verás a continuación 😉

Controlador ContactosController

Agregar esta clase que será el controlador para la entidad Contactos

Por el momento este controlador tendrá dos endpoints: Un Get(id) y un Post

    [ApiController]
    [Route("api/prospectos/{prospectoId:int}/comentarios")] //Ojo con la ruta dependiente, desde aquí se obtiene el prospectoId
    public class ContactosController : ControllerBase
    {
        private readonly ApplicationDBContext context;
        private readonly IMapper mapper;

        public ContactosController(ApplicationDBContext context, IMapper mapper)
        {
            this.context = context;
            this.mapper = mapper;
        }

        [HttpGet]
        public async Task<ActionResult<List<ContactoDTO>>> Get(int prospectoId)
        {
            var existeProspecto = await context.Prospectos.AnyAsync(x => x.Id == prospectoId);

            if (!existeProspecto)
                return NotFound("El prospecto no existe");

            var contactos = await context.Contactos.Where(x => x.ProspectoId == prospectoId).ToListAsync();

            return mapper.Map<List<ContactoDTO>>(contactos);
        }

        [HttpPost]
        public async Task<ActionResult> Post(int prospectoId, ContactoCreacionDTO contactoCreacionDTO)
        {
            var existeProspecto = await context.Prospectos.AnyAsync(x => x.Id == prospectoId);

            if (!existeProspecto)
                return NotFound("El prospecto no existe");

            var contacto = mapper.Map<Contacto>(contactoCreacionDTO);
            contacto.ProspectoId = prospectoId;

            context.Contactos.Add(contacto);
            await context.SaveChangesAsync();
            return Ok();
        }
    }

Añadir los perfiles de mapeo AutoMapper

Vamos a la clase AutoMapperProfile y añadimos:

    public class AutoMapperProfile : Profile
    {
        public AutoMapperProfile()
        {
            //Aquí van las reglas de mapeo <origen,destino>
            CreateMap<AgenteCreacionDTO, Agente>();
            CreateMap<Agente, AgenteDTO>();
            CreateMap<ProspectoCreacionDTO, Prospecto>();
            CreateMap<Prospecto,ProspectoDTO>();
            CreateMap<ContactoCreacionDTO, Contacto>();
            CreateMap<Contacto, ContactoDTO>();            
        }
    }

Pruebas en Swagger

Una vez hecho todo esto, toca que pruebes en Swagger, veamos:

Aprovechamos para actualizar la propiedad Title dentro de la configuración de Swagger en la clase Startup y el método ConfigureServices le pondré Sistema CRM😉

Tenemos esto, genial!

He creado tres prospectos:

Probaremos entonces un error, trataré de obtener los contactos del prospecto con Id que no existe, digamos Id = 4

Muy bien, está validando bien 😉.

Ahora creo un par de contactos y luego obtendré los contactos realizados a nuestro prospecto con Id = 3 llamada Rosa Martínez (el nombre de mi madre 😘)

Y se nos retorna lo correcto, los dos contactos que tiene, GENIAL! 😎

Nuestro API ya va tomando forma crack! 😀

Seguiremos en la próxima entrega aprendiendo más cosas interesante, y que te darán de comer mi estimado lector así que a echarle ganas 😉

Seguro que estás feliz como estos devs 😁

Si esta entrada te ha encantado, seguro que sí, entonces ya sabes qué hacer crack! Comparte 😉

2 comentarios en «Creando un API RESTFul Completo con Net Core para un Sistema CRM desde cero (3 de 5)»

Deja una respuesta

Tu dirección de correo electrónico no será publicada.