Entity Framework Core: Modelado, Herencia, Alta y Baja de Estudiantes en ASP.NET MVC

Informática y Telecomunicaciones8,16 KB

2. En base al modelo del punto 1:

a) Tablas que generaría EF

Cursos (PK = Id, FK = ResponsableId, FK = SuplenteId)
Modulos (PK = Id, FK = CursoId)
Docentes (PK = Id)
AreasTematicas (PK = Id)
AreaTematicaCurso (FK = AreasTematicasId, FK = CursosId)
Estudiantes (PK = Id)
Inscripciones (PK = Id, FK = EstudianteId, FK = CursoId)

b) Resolución de la herencia por defecto

EF usa una sola tabla para el padre y sus hijas (Cursos). Agrega una columna Discriminator que indica el tipo de cada fila, y las columnas de cada hija quedan en NULL en las filas del otro tipo.

c) Cadena de conexión

Server=SRV-ACADEMIA\SQLEXPRESS;Database=AcademiaDB;User Id=admin;Password=Ort2026

Server=(local)\SQLEXPRESS;Database=LibreriaM3BAgosto2026;Trusted_Connection=True;TrustServerCertificate=True

Ejercicio 3. Contexto de Entity Framework

Escriba la clase de contexto de EF con los DbSet necesarios para que EF realice el mapeo de las entidades modeladas en el punto 1. Incluya lo necesario para que el email del estudiante y el código del curso sean únicos en la base de datos.

public DbSet<Curso> Cursos { get; set; }
public DbSet<Presencial> Presenciales { get; set; }
public DbSet<Virtual> Virtuales { get; set; }
public DbSet<Modulo> Modulos { get; set; }
public DbSet<Docente> Docentes { get; set; }
public DbSet<AreaTematica> AreasTematicas { get; set; }
public DbSet<Estudiante> Estudiantes { get; set; }
public DbSet<Inscripcion> Inscripciones { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Estudiante>().HasIndex(e => new { e.Email }).IsUnique();
    modelBuilder.Entity<Curso>().HasIndex(c => new { c.Codigo }).IsUnique();
}

Ejercicio 4. Alta de estudiante

a) Presentación MVC: solamente las acciones del controlador para el alta. b) Lógica de aplicación: interfaces y casos de uso. No se permite un email ya existente. c) Acceso a datos: interfaces e implementación del repositorio, con los métodos auxiliares de búsqueda que utilice.

Interfaces

//ICUAltaEstudiante
public void Ejecutar(DTOAltaEstudiante dto);

//IRepositorioEstudiante
public void Add(Estudiante e);
public Estudiante FindByEmail(string email);

Controlador

//EN EstudianteController
private ICUAltaEstudiante _cuAltaEstudiante;

public EstudianteController(ICUAltaEstudiante cuAltaEstudiante)
{
    _cuAltaEstudiante = cuAltaEstudiante;
}

[HttpPost]
public IActionResult Create(DTOAltaEstudiante dto)
{
    try
    {
        _cuAltaEstudiante.Ejecutar(dto);
        ViewBag.msg = "Alta correcta";
    }
    catch (EstudianteYaExisteException)
    {
        ViewBag.msg = "Ya existe un estudiante con ese email";
    }
    catch (Exception)
    {
        ViewBag.msg = "Error";
    }
    return View();
}

Caso de uso

//EN CUAltaEstudiante
private IRepositorioEstudiante _repoEstudiante;

public CUAltaEstudiante(IRepositorioEstudiante repoEstudiante)
{
    _repoEstudiante = repoEstudiante;
}

public void Ejecutar(DTOAltaEstudiante dto)
{
    if (_repoEstudiante.FindByEmail(dto.Email) != null)
    {
        throw new EstudianteYaExisteException();
    }
    Estudiante e = MapperEstudiante.FromDtoAltaEstudianteToEstudiante(dto);
    e.Validar();
    _repoEstudiante.Add(e);
}

Repositorio

//EN RepositorioEstudiante
private ApplicationDbContext _context;

public RepositorioEstudiante(ApplicationDbContext context)
{
    _context = context;
}

public void Add(Estudiante e)
{
    _context.Estudiantes.Add(e);
    _context.SaveChanges();
}

public Estudiante FindByEmail(string email)
{
    return _context.Estudiantes.Where(e => e.Email == email).FirstOrDefault();
}

Ejercicio 5. Baja de estudiante

a) Presentación MVC: solamente las acciones del controlador para la baja. b) Lógica de aplicación: interfaz y caso de uso. Si el estudiante no existe, se debe lanzar una excepción.

Interfaces

//ICUBajaEstudiante
public void Ejecutar(int id);

//IRepositorioEstudiante (se agregan)
public Estudiante FindById(int id);
public void Remove(Estudiante e);

Controlador

//EN EstudianteController
private ICUBajaEstudiante _cuBajaEstudiante;

public EstudianteController(ICUBajaEstudiante cuBajaEstudiante)
{
    _cuBajaEstudiante = cuBajaEstudiante;
}

[HttpPost]
public IActionResult Delete(int id)
{
    try
    {
        _cuBajaEstudiante.Ejecutar(id);
        ViewBag.msg = "OK";
    }
    catch (EstudianteNoExisteException)
    {
        ViewBag.msg = "El estudiante no existe";
    }
    catch (Exception)
    {
        ViewBag.msg = "Error";
    }
    return View();
}

Caso de uso

//EN CUBajaEstudiante
private IRepositorioEstudiante _repoEstudiante;

public CUBajaEstudiante(IRepositorioEstudiante repoEstudiante)
{
    _repoEstudiante = repoEstudiante;
}

public void Ejecutar(int id)
{
    Estudiante e = _repoEstudiante.FindById(id);
    if (e == null)
    {
        throw new EstudianteNoExisteException();
    }
    _repoEstudiante.Remove(e);
}

Ejercicio 6. Inyección de dependencias

Agregue lo necesario para la inyección de las dependencias en cada capa y lo que escribiría en la clase Program para configurarlas (alta y baja).

builder.Services.AddScoped<IRepositorioEstudiante, RepositorioEstudiante>();
builder.Services.AddScoped<ICUAltaEstudiante, CUAltaEstudiante>();
builder.Services.AddScoped<ICUBajaEstudiante, CUBajaEstudiante>();

Ejercicio 7. Value Object de contacto

Implemente un Value Object para los datos de contacto del estudiante (teléfono y dirección): el teléfono no puede ser nulo o vacío, debe tener exactamente 9 caracteres y comenzar con "09"; la dirección no puede ser nula o vacía, ni tener menos de 5 caracteres. Asegure la inmutabilidad y la validación.

[ComplexType]
public record Contacto
{
    public string Telefono { get; init; }
    public string Direccion { get; init; }

    public Contacto(string telefono, string direccion)
    {
        Telefono = telefono;
        Direccion = direccion;
        Validar();
    }

    public void Validar()
    {
        if (string.IsNullOrEmpty(Telefono))
        {
            throw new DatoVacioException("El teléfono es obligatorio");
        }
        if (Telefono.Length != 9 || !Telefono.StartsWith("09"))
        {
            throw new TelefonoInvalidoException();
        }
        if (string.IsNullOrEmpty(Direccion))
        {
            throw new DatoVacioException("La dirección es obligatoria");
        }
        if (Direccion.Length < 5)
        {
            throw new LargoDireccionInvalidoException();
        }
    }
}

Ejercicio 8. Clase de dominio Estudiante

Escriba la clase de dominio Estudiante incluyendo lo necesario para validar que el nombre tenga entre 2 y 50 caracteres y que el apellido y el email sean obligatorios. Incluya la propiedad para el Value Object definido en el punto anterior.

public class Estudiante
{
    public int Id { get; set; }
    public string Nombre { get; set; }
    public string Apellido { get; set; }
    public string Email { get; set; }
    public Contacto Contacto { get; set; }

    public void Validar()
    {
        if (string.IsNullOrEmpty(Nombre))
        {
            throw new DatoVacioException("El nombre es obligatorio");
        }
        if (Nombre.Length < 2 || Nombre.Length > 50)
        {
            throw new LargoNombreInvalidoException();
        }
        if (string.IsNullOrEmpty(Apellido))
        {
            throw new DatoVacioException("El apellido es obligatorio");
        }
        if (string.IsNullOrEmpty(Email))
        {
            throw new DatoVacioException("El email es obligatorio");
        }
        if (Contacto == null)
        {
            throw new DatoVacioException("El contacto es obligatorio");
        }
        Contacto.Validar();
    }
}

Descargar

PDF Word HTML