¿Cómo estructurar y gestionar un CRUD en una base de datos?
En el mundo del desarrollo de software, la gestión de datos es vital, y para ello se utiliza comúnmente el CRUD, que facilita la creación, lectura, actualización y eliminación de registros en una base de datos. Vamos a detallar los pasos esenciales y las recomendaciones para construir un CRUD que sea eficiente y funcional, poniendo especial énfasis en la importancia de optar por un diseño que minimice la eliminación directa de datos, priorizando su desactivación.
¿Qué es la estructura CRUD y cómo implementarla?
CRUD son las siglas de Create, Read, Update, y Delete, las operaciones básicas que cualquier aplicación debería poder realizar sobre los datos. Implementar un CRUD implica programar métodos que permitan estas operaciones en un sistema de gestión de bases de datos.
Creación de Datos
Crear (Create): Se refiere a insertar nuevos registros en la base de datos.
publicvoidCrearCategoria(Categoría objetoCategoría){using(var db =newInventarioContexto()){ db.Categorías.Add(objetoCategoría); db.SaveChanges();}}
Este método CrearCategoria agrega un objeto de tipo Categoría a la tabla de categorías y guarda los cambios en la base de datos.
Lectura de Datos
Leer (Read): Consiste en recuperar datos, usualmente para desplegarlos al usuario.
publicList<Categoría>ObtenerListaCategorias(){using(var db =newInventarioContexto()){return db.Categorías.ToList();}}
Este método devuelve una lista de todas las entradas en la tabla de categorías.
Actualización de Datos
Actualizar (Update): Es la modificación de registros existentes.
publicvoidActualizarCategoria(Categoría objetoCategoría){using(var db =newInventarioContexto()){ db.Categorías.Update(objetoCategoría); db.SaveChanges();}}
Actualiza los datos de una categoría específica basándote en su identificador.
¿Por qué es importante gestionar la eliminación de datos cuidadosamente?
Eliminar datos directamente puede conllevar riesgos significativos en bases de datos relacionales debido a las dependencias de clave foránea y la necesidad de mantenimientos históricos. Recomiendo siempre explorar alternativas como marcar los datos como inactivos en lugar de eliminarlos. Esto se puede hacer usando un campo booleano.
Eliminar (Delete): En lugar de borrar un registro, cambiar su estado a inactivo.
Esto preserva los datos por si en el futuro necesitas recuperar información antigua o evitar problemas de integridad referencial.
¿Qué consideraciones adicionales tener en cuenta al implementar CRUD?
Manejadores de errores:
Siempre implementa excepciones para manejar errores inesperados durante las operaciones de bases de datos.
Optimización de consultas:
Minimiza las operaciones que devuelven grandes volúmenes de datos, y usa técnicas como paginación para mejorar el rendimiento.
Buen diseño de clases y métodos:
Mantén la simplicidad y claridad en tus métodos para cada operación CRUD, lo que facilita su mantenimiento y extensibilidad.
Documentación:
Documenta cada método suficientemente para que otros desarrolladores entiendan la lógica aplicada.
Adoptar este enfoque estructurado y prevenir eliminación directa permite llevar un control más robusto y preciso de los datos gestionados en aplicaciones desarrolladas. Con estas bases, estás preparado para manejar los ciclos de vida de los datos de una manera efectiva y segura. ¡Continúa explorando y aplicando estas técnicas para mejorar tus habilidades!
La conexión queda abierta si no le dices que se cierre (método Dispose()). La llave de cierre de la sentencia Using se encarga de eso.
Para escribir el código como mencionas y así evitar la sentencia Using tendrías que configurar el Context como un servicio mediante el método ConfigureServices(IServiceCollection services) de la clase Startup.cs del proyecto principal.
Los sitios creados con Blazor son responsive?
Que yo sepa están construidos con bootstrap, asi que si.
todos los métodos son los mismos en cada clase de la capa de negocio, hay una forma de crear un clase que acepte el tipo de objeto y el parámetro del objeto y solo sea llamarlo por ejemplo en B_Product para que ejecute los métodos y así evitar repetir código?, lo he intentado pero no he podido.
Sí, a ese objeto se le llama Interface y sirve justo para lo que mencionas
Si, pero tendrías que trabajar con herencia, clases genéricas e Interfaces. Esta sera una explicaión muy larga, pero espero que te sea de utilidad.
Primero necesitamos que Category herede de una clase base (BaseEntity), la cual que no necesariamente tiene que llevar código; Sin embargo, en lo personal prefiero que esta clase base lleve ID y un booleano que sirva de bandera de habilitado, ya que con esto obligo a que todas mis otras clases lleven estos 2 datos.
Para efectos del curso la clase base quedaría así:
publicclassBaseEntity{}
y se modifica category a:
publicclassCategoryEntity:BaseEntity//Aquí category hereda de BaseEntity{//Aqui va el resto del código de la entidad//No es importante listarlo en este momento}
Ahora necesitamos una interfaz para definir los metodos que van a llevar nuestras clases de Business. En nuestro ejemplo llamaremos a esta interfaz IBusinessBase
publicinterfaceIBusinessBase<T> where T:BaseEntity,new(){publicList<T>List();//Se regresa una lista con elementos de tipo TpublicvoidCreate(T objectInstance);//Se acepta un objeto de tipo TpublicvoidUpdate(T objectInstance);//Se acepta un objeto de tipo TpublicvoidDelete(T objectInstance);//Se acepta un objeto de tipo T}
Analicemos la sentencia
publicinterfaceIBusinessBase<T> where T:BaseEntity,new()
En la parte que dice IBusinessBase<T> estamos diciendo que puede aceptar un objeto de tipo T.
Luego con where T : BaseEntity, new() hacemos 2 cosas:
Restringimos a que T sea de tipo BaseEntity. CategoryEntity al ser una subclase de BaseEntity cumple este criterio.
Al poner el new() permitimos que se generen nuevas instancias de la clase que tenga T.
Lo próximo que se necesita hacer es implementar la interfaz con una clase genérica (BusinessBase), la cual quedaría así:
publicclassBusinessBase<T>:IBusinessBase<T> where T:CategoryEntity,new(){//Usamos virtual para que, en caso de ser necesario, podamos hacer un override en las clases derivadaspublic virtual List<T>List(){using(var context =newInventoryContext()){return context.Set<T>().ToList();//Set<T>() crea un DbSet de tipo T }}public virtual voidCreate(T objectInstance){using(var context =newInventoryContext()){ context.Set<T>().Add(objectInstance);//Set<T>() crea un DbSet de tipo T context.SaveChanges();}}public virtual voidUpdate(T objectInstance){using(var context =newInventoryContext()){ context.Set<T>().Update(objectInstance);//Set<T>() crea un DbSet de tipo T context.SaveChanges();};}public virtual voidDelete(T objectInstance){//Este no se manejo en este momento}}
Analicemos el código de la clase:
en la parte que dice class BusinessBase<T> : IBusinessBase<T> where T : CategoryEntity, new() estamos diciendo que BusinessBase implementa a la interfaz IBusinessBase, donde ambas pueden aceptar el tipo T que es un BaseEntity y que puede crear instancias nuevas.
Algo recurrente en el código es el uso de context.Set<T>() que devuelve una instancia de DbSet<T> que permite acceder a las entidades de tipo T del contexto y la base de datos. El uso de este método nos permite acceder al DBSet declarado en el contexto sin saber el nombre del mismo. Por ejemplo:
context.Set<CategoryEntity>().ToList() seria similar a context.Categories.ToList()
Crearemos la clase B_Category
Al final lo único que necesitaremos hacer será declarar la clase B_Category reemplazando T por CategoryEntity.
publicclassB_Category:BusinessBase<CategoryEntity>{//En caso de ser necesario podriamos hacer overrides aqui}
Esto se puede realizar porque T es de tipo BaseEntity y CategoryEntity hereda (o es derivado) de BaseEntity, por lo cual cumple con este requisito.
Si hacemos que las demás Entidades hereden de BaseEntity podremos declarar sus clases como:
En .NET MVC estaba muy acostumbrado a pasar datos de la Vista al Controlador, y en el controlador realizar una llamada a la BD y/o una clase que hiciera la llamada a la BD.
Esta técnica me parece más extensa, mejor elaborada, código más limpio y elegante.
¿Que libro o web site me recomienda para aprender mucho más sobre las sentencias Linq?
De antemano agradezco sus respuestas, muchas gracias.
Los libros de O´Relly suelen ser buenos tal vez te sirva Linq Pocket Reference: Learn and Implement Linq for .Net Applications de Joseph Albahari y Ben Albahari o también toda la serie "in Action" como LINQ in Action
¿En lugar de usar using (var db = new InventaryContext()). Puedo importar el context mediante el patrón de injección de dependencias?
¡Hola! :D
¿Cuál es el problema que tienes?, ¿en qué te puedo ayudar? Compártenos tu código por favor. Puedes adjuntar imágenes arrastrándolas a esta ventana de comentario.
Nunca pares de aprender 💚
clase magistral!!
la mejor forma de implementar este crud es a traves de Injeccion de dependencia
using BlazorServerInventory.Business;using BlazorServerInventory.Data;using DataAccess;using Microsoft.AspNetCore.Components;using Microsoft.AspNetCore.Components.Web;var builder =WebApplication.CreateBuilder(args);// Add services to the container.builder.Services.AddRazorPages();builder.Services.AddServerSideBlazor();builder.Services.AddSingleton<WeatherForecastService>();builder.Services.AddSqlServer<InventoryContext>(builder.Configuration.GetConnectionString("cnTareas"));//builder.Services.AddDbContext<InventoryContext>();builder.Services.AddScoped<ICategory,B_Category>();var app = builder.Build();// Configure the HTTP request pipeline.if(!app.Environment.IsDevelopment()){ app.UseExceptionHandler("/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.MapBlazorHub();app.MapFallbackToPage("/_Host");app.Run();
buenas noches, me podrias ayudar en la construccion de la bases de datos al momento de la migracion. gracias
Adjunto el crud mejorado, con el codigo
publicclassB_Warehouse{publicList<WarehouseEntity>WarehouseList(){using(var db =newInventaryContext()){return db.Warehouses.ToList();}}publicvoidCRUDWarehouse(WarehouseEntity oWarehouse,IOperator op){using(var db =newInventaryContext()){switch(op){caseIOperator.Create: db.Warehouses.Add(oWarehouse); db.SaveChanges();break;caseIOperator.Update: db.Warehouses.Update(oWarehouse); db.SaveChanges();break;}}}}}```
Realice una clase ENUM para seleccionar el tipo del crud
publicenumIOperator{Create,Update,Delete}
Genial
Para el eliminado pueden usar el concepto llamado softdelete.
¿en este proyecto se puede hacer scaffolding para no tener que hacer el crud manualmente?