Cómo hacer un Mantenedor CRUD con .Net Core MVC
Si quieres aprender tienes que hacer cosas, un excelente inicio sería hacer un mantenedor completo con .Net Core MVC, y consolida toda la teoría aprendida usándola junta 😉

En este taller vamos a crear un mantenedor de clientes que te permitirá consolidar todo lo aprendido en la teoría.

Las tecnologías y los temas que abarcaré son los siguientes:

  • WEB APP .NET CORE
  • Patrón MVC
  • Patrón Inyección de dependencias
  • Bootstrap
  • Razor
  • SQL Server
  • Entity Framework Core

Configurando proyecto

Crear proyecto en Visual Studio, en mi caso utilizaré 2019

El tipo del proyecto es:

ASP.NET Core Web App (Model-View-Controller)

Yo le pondré de nombre MyCrud

El Target Framework a utilizar será .NET Core 3.1 y dejamos las demás configuraciones que Visual Studio trae por defecto.

Mi proyecto recién creado

Instalando dependencias

Todo proyecto es muy probable que no sólo utilice las librerías que por defecto nos trae Net Framework o Net Core, sino que también sea necesario utilizar librerías adicionales, que permitan agregarle más funcionalidades, en el caso de este proyecto utilizaremos algunas así que hay que instalarlas desde Nuget.

  • Microsoft.EntityFrameworkCore
  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.Tools

En este momento utilizaré para las tres la versión 5.0.10

En versiones previas de Net Core no hacía falta instalar el EntityFrameworkCore.SqlServer ni el EntityFrameworkCore.Tools ya que venían incluídos en el EntityFrameworkCore

Entidades

Utilizaremos Entity Framework así es que una entidad dará origen a una tabla en la base de datos debido a que utilizaré Entity Framework Core con el estilo Code First.

Crear una carpeta en el proyecto y le llamaremos Entities y nuestra primera entidad llamada Client

La forma más profesional y apropiada sería crear las entidades como una librería de clases y que sea llamada como una dependencia para hacer el código más modular y reutilizable, pero como en esta entrada la finalidad es ponernos en marcha rápidamente, no lo haremos así. De igual forma estaremos aplicando buenas prácticas usando MVC e Inyección de dependencias así que no te preocupes mucho 😉

    public class Client
    {
        [Key] //esto hace que este campo sea primary key en la BD
        public int Id { get; set; }

        [Column(TypeName = "varchar(75)")]
        [Display(Name = "Nombres")]
        [Required(ErrorMessage = "El campo nombres es obligatorio")]
        public string Nombres { get; set; }

        [Column(TypeName = "varchar(75)")]
        [Display(Name = "Apellidos")]
        [Required(ErrorMessage = "El campo apellidos es obligatorio")]
        public string Apellidos { get; set; }

        [Column(TypeName = "varchar(50)")]
        [Display(Name = "Departamento")]
        public string Departamento { get; set; }

        [Column(TypeName = "varchar(50)")]
        [Display(Name = "Pais")]
        public string Pais { get; set; }

        [Column(TypeName = "datetime")]
        [Display(Name = "Fecha de Ingreso")]
        [Required(ErrorMessage = "El campo fecha de ingreso es obligatorio")]
        public DateTime FechaIngreso { get; set; }
    }

Estoy haciendo uso de las Data annotations, son aquellas que están entre corchetes, y nos sirven para poder ayudar a EntityFramework en el momento de crear las tablas en la base de datos cuando hagamos una migración y en el UI al momento de mostrar campos y validar campos.

Contexto y Cadena de conexión

Un contexto en EF es una clase especial, que nos proporcionará la conexión al origen de datos.

En este proyecto mi contexto se llamará ApplicationDBContext

    public class ApplicationDBContext : DbContext
    {
        public ApplicationDBContext(DbContextOptions options) : base(options)
        {
        }
        public DbSet<Client> Clients { get; set; }
    }

La cadena de conexión va en el archivo appsettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "default": "server=.;initial catalog=Test2509;integrated security=true"
  }
}

Asegúrate que tus credenciales a tu Gestor de Base de datos sean correctos

Inyección de dependencias

Este tema es muy importante, te invito a leer este otro post que escribí hace un tiempito acerca de este patrón de diseño.

La forma de configurar la Inyección de dependencias es agregando una línea de código en la clase Startup.cs y el método ConfigureServices

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();
            services.AddDbContext<ApplicationDBContext>(options=>
                options.UseSqlServer(Configuration.GetConnectionString("default"))
            );
        }

Estas líneas de código hacen posible que el contexto se inyecte en todos los controladores, ahora net core permite de forma nativa este patrón de diseño, antes era necesario utilizar librerías de terceros, hoy no 😊.

Migraciones

Las migraciones son los procedimientos mediante el cual EF costruye los objetos de base de datos a partir de nuestro código.

Para trabajar con migraciones debemos entrar en su consola llamada Package Manager console

y ejecutar los siguientes comandos, uno después del otro

add-migration Initial

update-database

Ejecutando comandos de migraciones

Si revisamos la base de datos y no hemos tenido ningún error en la ejecución de los comandos, vamos a ver que la tabla Client fue creada según definimos nuestra entidad

Tabla migrada por EF

Para futuras migraciones repetimos el proceso

Controladores

Crear controlador ClientsController e inyectar el contexto

    public class ClientsController : Controller
    {
        private readonly ApplicationDBContext context;

        public ClientsController(ApplicationDBContext context)
        {
            this.context = context;
        }

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

Vistas

Crearemos la vista, dando clic derecho al controlador y Add view...

El nombre lo dejaremos por defecto en Index.cshtml

Sólo para testear, incluir un trozo de html en él

<h1>Hola mundo</h1>
El proyecto va quedando así, ojo con la estructura y carpetas del proyecto

Correr el proyecto con

CTRL + F5

Navegamos hacia la ruta clients

Añadiendo funcionalidades al controlador

Sólo queda un último tramo por recorrer! Photo by Josh Hild on Unsplash

Creamos los métodos AddEdit tanto para GET como para POST y el método Delete.

    public class ClientsController : Controller
    {
        private readonly ApplicationDBContext context;

        public ClientsController(ApplicationDBContext context)
        {
            this.context = context;
        }
        public IActionResult Index()
        {
            return View(context.Clients.ToList());
        }

        [HttpGet]
        public async Task<IActionResult> AddEdit(int id = 0)
        {
            var cliente = await context.Clients.FindAsync(id);
            return View(cliente);
        }

        [HttpPost]
        public async Task<IActionResult> AddEdit(Client client)
        {
            if (ModelState.IsValid)
            {
                if (client.Id == 0)
                    context.Add(client);
                else
                    context.Update(client);
                await context.SaveChangesAsync();
                return RedirectToAction(nameof(Index));
            }
            return View(client);
        }


        public async Task<IActionResult> Delete(int id)
        {
            var cliente = await context.Clients.FindAsync(id);
            context.Clients.Remove(cliente);
            await context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }
    }

Personalizando vistas

Vamos a hacer uso de font-awesome que es una librería de assets para íconos para embellecer los controles de la UI.

La agregamos al proyecto en el archivo _Layout.cshtml dentro de la carpeta Shared

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - MyCrud</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" />
    <script src="https://kit.fontawesome.com/e0012eeb51.js" crossorigin="anonymous"></script>
</head>

Creamos una vista al método AddEdit y la llamaremos AddEdit.cshtml

Vamos a personalizar Index.cshtml

@model IEnumerable<MyCrud.Entities.Client>
@{
    ViewData["Title"] = "Inicio";
}
<h4>Listado de Clientes</h4>
<hr />

<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Nombres)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Apellidos)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Departamento)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.FechaIngreso)
            </th>
            <th>
                <a class="btn btn-outline-success" asp-action="AddEdit"><i class="fa fa-plus-square"></i> Cliente</a>
            </th>
        </tr>
    </thead>
    <tbody>
        @foreach(var item in Model)
        {
        <tr>
            <td>
                @Html.DisplayFor(model => item.Nombres)
            </td>
            <td>
                @Html.DisplayFor(model => item.Apellidos)
            </td>
            <td>
                @Html.DisplayFor(model => item.Departamento)
            </td>
            <td>
                @Html.DisplayFor(model => item.FechaIngreso)
            </td>
            <td>
                <a asp-action="AddEdit" asp-route-id="@item.Id"><i class="fa fa-marker fa-lg"></i></a>
                <a asp-action="Delete" asp-route-id="@item.Id" class="text-danger ml-1"
                   onclick="return confirm('¿Está seguro que desea eliminar este registro?')"><i class="fa fa-trash-alt fa-lg"></i></a>
            </td>
        </tr>
        }
    </tbody>
</table>

Vamos a personalizar AddEdit.cshtml

@model MyCrud.Entities.Client
@{
    ViewData["Title"] = "Mantenimiento";
}
<h4>Mantenedor de Clientes</h4>
<hr />

<div class="row">
    <div class="col-md-6">
        <form asp-action="AddEdit">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-row">
                <div class="form-group col-md-6">
                    <label asp-for="Nombres" class="control-label"></label>
                    <input asp-for="Nombres" class="form-control" />
                    <span asp-validation-for="Nombres" class="text-danger"></span>
                </div>
                <div class="form-group col-md-6">
                    <label asp-for="Apellidos" class="control-label"></label>
                    <input asp-for="Apellidos" class="form-control" />
                    <span asp-validation-for="Apellidos" class="text-danger"></span>
                </div>
            </div>
            <div class="form-group">
                <label asp-for="Pais" class="control-label"></label>
                <input asp-for="Pais" class="form-control" />
                <span asp-validation-for="Pais" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Departamento" class="control-label"></label>
                <input asp-for="Departamento" class="form-control" />
                <span asp-validation-for="Departamento" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="FechaIngreso" class="control-label"></label>
                <input asp-for="FechaIngreso" class="form-control" type="date" />
                <span asp-validation-for="FechaIngreso" class="text-danger"></span>
            </div>

            <div class="form-row">
                <div class="form-group col-md-6">
                    <input type="submit" value="Enviar" class="btn btn-primary btn-block" />
                </div>
                <div class="form-group col-md-6">
                    <a asp-action="Index" class="btn btn-secondary btn-block"><i class="fa fa-table"></i> Volver a listado</a>
                </div>
            </div>

        </form>
    </div>
</div>

@*Se agrega esta sección para que funcione la validación frontend*@
@section Scripts{ 
    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Si ahora mismo corremos el proyecto, se nos lanzará el Index del controlador Home, para ver nuestra vista Clients podríamos escritir esta ruta en la barra de navegación, pero si queremos configurar la vista Clients por defecto modificaremos una línea de código en el método Configure de la clase Startup

        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=Clients}/{action=Index}/{id?}");
            });
        }

Al final el proyecto quedó con esta estructura:

Probando la web app

Ejecutamos como ya sabemos con

CTRL + F5

Agregaremos un cliente

Al guardar el registro seremos redirigidos al Index

Se nos muestra en la grilla y lo podemos confirmar en la BD
Probando eliminar un registro

Listo! Tenemos un mantenedor CRUD completamente funcional y lo más importante, hemos consolidado muchos conocimientos tales como el patrón MVC, entity framework, inyección de dependencias, bootstrap, razor, una línea de javascript y algo muy importante pero que no se habla mucho, el COMO TODAS ESTAS TECNOLOGÍAS SE USAN JUNTAS 🤯, ese es el camino crack, haz proyectos pequeños y anda agregándoles funcionalidad.

Para el presente proyecto me inspiré en algunos tutoriales que vi en Youtube por lo cual te recomiendo que complementes lo que aprendiste aquí con más retos de proyectos ya sea en plataformas como Youtube o cursos de Udemy.

Repite conmigo: el Crud MVC, Reto desbloqueado! 😎😄

Si esta entrada te ha encantado 😍 considera compartirla en tus redes sociales, de esa forma esparcirás el conocimiento y además despertará en ti ese bichito de compartir el conocimiento, hasta la vista cracks!

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *