using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System.Linq; using System.Reflection; using Microsoft.EntityFrameworkCore; using System.ComponentModel.DataAnnotations; using System.Linq.Expressions; using System.Linq.Dynamic.Core; using System.Runtime.CompilerServices; using Serialize.Linq.Serializers; namespace SwaggerColegiados.Controllers { [ApiController] [Route("api/[controller]")] public abstract class GenericoController : ControllerBase where TEntity : class { /// /// Obtiene todos los registros de la entidad (Solo Lectura). /// /// Lista de entidades. [Authorize] [HttpGet] public virtual async Task GetAll() { string operacion = nameof(GetAll); string usuario = User?.Identity?.Name ?? "Usuario no autenticado"; try { // Asegúrate de pasar UseLazyLoadingProxies: false para desactivar Lazy Loading using (var context = bdEmtusa.tscEmtusa.NuevoContexto()) { var dbSet = context.Set(); var entities = await dbSet.AsNoTracking().ToListAsync(); //string jsonString = JsonSerializer.Serialize(entities); //RegistrarActividad(operacion, usuario, "Sin parámetros", $"Registros obtenidos: {entities.Count}"); return Ok(entities); } } catch (Exception ex) { return StatusCode(500, $"Error interno del servidor: {ex.Message}"); } } /// /// Obtiene un registro específico por ID (Solo Lectura). /// /// ID de la entidad. /// Entidad encontrada. [Authorize] [HttpGet("{id}")] public virtual IActionResult GetById(TKey id) { string operacion = nameof(GetById); string usuario = User?.Identity?.Name ?? "Usuario no autenticado"; try { using (var context = bdEmtusa.tscEmtusa.NuevoContexto()) { var dbSet = context.Set(); var entity = dbSet.Find(id); if (entity == null) { //RegistrarActividad(operacion, usuario, $"ID: {id}", null, "Entidad no encontrada"); return NotFound(); } //RegistrarActividad(operacion, usuario, $"ID: {id}", "Entidad encontrada"); return Ok(entity); } } catch (Exception ex) { //RegistrarActividad(operacion, usuario, $"ID: {id}", null, ex.Message); return StatusCode(500, $"Error interno del servidor: {ex.Message}"); } } [Authorize] [HttpPost("filtrar")] public virtual async Task Filtrar([FromBody] ExpressionWrapper request) { string operacion = nameof(Filtrar); string usuario = User?.Identity?.Name ?? "Usuario no autenticado"; string parametros = request.Expression; try { using (var context = bdEmtusa.tscEmtusa.NuevoContexto()) { // Crear el deserializador var serializer = new ExpressionSerializer(new Serialize.Linq.Serializers.JsonSerializer()); // Deserializar la expresión var deserializedExpression = serializer.DeserializeText(request.Expression) as Expression>; if (deserializedExpression == null) { //RegistrarActividad(operacion, usuario, parametros, null, "Expresión inválida"); return BadRequest("La expresión deserializada es nula o incorrecta."); } // Aplicar el filtro en la consulta var query = context.Set().Where(deserializedExpression); // Agregar un log para verificar el SQL generado y la consulta Console.WriteLine(query.ToQueryString()); // Esto imprime la consulta SQL generada por EF Core var res = await query.AsNoTracking().ToListAsync(); // Verificar si el resultado es vacío if (res.Count == 0) { Console.WriteLine("La consulta no devolvió resultados."); } //RegistrarActividad(operacion, usuario, parametros, $"Registros encontrados: {res.Count}"); return Ok(res); } } catch (Exception ex) { //RegistrarActividad(operacion, usuario, parametros, null, ex.Message); return StatusCode(500, $"Error interno del servidor: {ex.Message}"); } } [Authorize] [HttpPost("filtrar-entidad")] public virtual async Task FiltrarEntidad([FromBody] ExpressionWrapper request) { string operacion = nameof(FiltrarEntidad); string usuario = User?.Identity?.Name ?? "Usuario no autenticado"; string parametros = request.Expression; try { using (var context = bdEmtusa.tscEmtusa.NuevoContexto()) { var serializer = new ExpressionSerializer(new Serialize.Linq.Serializers.JsonSerializer()); var deserializedExpression = serializer.DeserializeText(request.Expression) as Expression>; if (deserializedExpression == null) { //RegistrarActividad(operacion, usuario, parametros, null, "Expresión deserializada es nula o incorrecta."); return BadRequest("La expresión deserializada es nula o incorrecta."); } var entity = await context.Set() .Where(deserializedExpression) .AsNoTracking() .FirstOrDefaultAsync(); if (entity == null) { //RegistrarActividad(operacion, usuario, parametros, null, "No se encontró ninguna entidad que coincida con el filtro."); return NotFound("No se encontró ninguna entidad que coincida con el filtro."); } //RegistrarActividad(operacion, usuario, parametros, "Entidad encontrada."); return Ok(entity); } } catch (Exception ex) { //RegistrarActividad(operacion, usuario, parametros, null, ex.Message); return StatusCode(500, $"Error interno del servidor: {ex.Message}"); } } [Authorize] [HttpPost("filtrar-Atributo")] public virtual async Task FiltrarAtributoDinamico([FromBody] ExpressionWrapperAtributo request) { string operacion = nameof(FiltrarAtributoDinamico); string usuario = User?.Identity?.Name ?? "Usuario no autenticado"; string parametros = $"Expresión: {request.Expression}, Atributo: {request.AttributeName}"; try { using (var context = bdEmtusa.tscEmtusa.NuevoContexto()) { var serializer = new ExpressionSerializer(new Serialize.Linq.Serializers.JsonSerializer()); var expression = serializer.DeserializeText(request.Expression) as Expression>; if (expression == null) { //RegistrarActividad(operacion, usuario, parametros, null, "La expresión deserializada es nula o incorrecta."); return BadRequest("La expresión deserializada es nula o incorrecta."); } if (string.IsNullOrWhiteSpace(request.AttributeName)) { //RegistrarActividad(operacion, usuario, parametros, null, "El nombre del atributo es obligatorio."); return BadRequest("El nombre del atributo es obligatorio."); } var parameter = Expression.Parameter(typeof(TEntity), "x"); var property = Expression.PropertyOrField(parameter, request.AttributeName); if (property == null) { //RegistrarActividad(operacion, usuario, parametros, null, $"El atributo '{request.AttributeName}' no existe en la entidad."); return BadRequest($"El atributo '{request.AttributeName}' no existe en la entidad."); } // Crear una expresión para seleccionar el atributo var attributeSelector = Expression.Lambda>( Expression.Convert(property, typeof(object)), parameter); var entities = await context.Set() .Where(expression) .Select(attributeSelector) .AsNoTracking() .ToListAsync(); //RegistrarActividad(operacion, usuario, parametros, $"Registros obtenidos: {entities.Count}"); return Ok(entities); } } catch (Exception ex) { //RegistrarActividad(operacion, usuario, parametros, null, ex.Message); return StatusCode(500, $"Error interno del servidor: {ex.Message}"); } } /// /// Crea una nueva entidad (Escritura). /// /// Entidad a crear. /// Entidad creada. /// /// Crea una nueva entidad (Escritura). /// /// Entidad a crear. /// Entidad creada. [Authorize] [HttpPost] public virtual IActionResult Create([FromBody] TEntity entity) { string operacion = nameof(Create); string usuario = User?.Identity?.Name ?? "Usuario no autenticado"; string parametros = System.Text.Json.JsonSerializer.Serialize(entity); // Ignorar la validación automática del modelo if (!TryValidateModel(entity)) { ModelState.Clear(); // Limpia cualquier error de validación } try { using (var context = bdEmtusa.tscEmtusa.NuevoContexto()) { var dbSet = context.Set(); dbSet.Add(entity); context.SaveChanges(); } //RegistrarActividad(operacion, usuario, parametros, "Entidad creada exitosamente"); var id = GetEntityId(entity); if (id != null) { return CreatedAtAction(nameof(GetById), new { id = id }, entity); } else { return Ok(entity); } } catch (Exception ex) { //RegistrarActividad(operacion, usuario, parametros, null, ex.Message); return StatusCode(500, $"Error interno del servidor: {ex.Message}"); } } /// /// Actualiza una entidad existente (Escritura). /// /// ID de la entidad a actualizar. /// Entidad actualizada. /// No content. [Authorize] [HttpPut("{id}")] public virtual IActionResult Update(TKey id, [FromBody] TEntity entity) { string operacion = nameof(Update); string usuario = User?.Identity?.Name ?? "Usuario no autenticado"; string parametros = $"ID: {id}, Datos: {System.Text.Json.JsonSerializer.Serialize(entity)}"; var entityId = GetEntityId(entity); if (entityId == null || !id.Equals((TKey)entityId)) return BadRequest("El ID de la entidad no coincide con el ID de la URL."); try { using (var context = bdEmtusa.tscEmtusa.NuevoContexto()) { var dbSet = context.Set(); // Buscar la entidad existente var existingEntity = dbSet.Find(id); if (existingEntity == null) { // RegistrarActividad(operacion, usuario, parametros, null, "Entidad no encontrada"); return NotFound("No se encontró la entidad para actualizar."); } // Iterar sobre las propiedades de la entidad foreach (var property in typeof(TEntity).GetProperties()) { // Verificar si la propiedad es escalar o navegación var propertyMetadata = context.Model.FindEntityType(typeof(TEntity))? .FindProperty(property.Name); if (propertyMetadata == null) { // Es una propiedad de navegación, omitirla continue; } // Manejar propiedades escalares var newValue = property.GetValue(entity); var oldValue = property.GetValue(existingEntity); if (!Equals(newValue, oldValue)) { context.Entry(existingEntity).Property(property.Name).CurrentValue = newValue; } } // Guardar cambios context.SaveChanges(); //RegistrarActividad(operacion, usuario, parametros, "Actualización exitosa"); } return NoContent(); } catch (Exception ex) { //RegistrarActividad(operacion, usuario, parametros, null, ex.Message); return StatusCode(500, $"Error interno del servidor: {ex.Message}"); } } /// /// Elimina una entidad por ID (Escritura). /// /// ID de la entidad a eliminar. /// No content. [Authorize] [HttpDelete("{id}")] public virtual IActionResult Delete(TKey id) { string operacion = nameof(Delete); string usuario = User?.Identity?.Name ?? "Usuario no autenticado"; string parametros = $"ID: {id}"; try { using (var context = bdEmtusa.tscEmtusa.NuevoContexto()) { var dbSet = context.Set(); var entity = dbSet.Find(id); if (entity == null) { // RegistrarActividad(operacion, usuario, parametros, null, "Entidad no encontrada"); return NotFound(); } dbSet.Remove(entity); context.SaveChanges(); // RegistrarActividad(operacion, usuario, parametros, "Entidad eliminada exitosamente"); } return NoContent(); } catch (Exception ex) { // RegistrarActividad(operacion, usuario, parametros, null, ex.Message); return StatusCode(500, $"Error interno del servidor: {ex.Message}"); } } /// /// Obtiene el valor de la propiedad de ID de la entidad. /// /// Entidad de la cual obtener el ID. /// Valor del ID. protected virtual object GetEntityId(TEntity entity) { // Busca las propiedades que podrían ser claves primarias según las convenciones o el atributo [Key] var type = typeof(TEntity); // Primero, buscamos cualquier propiedad marcada con [Key], que es la forma más confiable de identificar el ID var idProperty = type.GetProperties().FirstOrDefault(p => p.GetCustomAttribute() != null); if (idProperty != null) { return idProperty.GetValue(entity); } // Si no hay un atributo [Key], intentamos con algunas convenciones de nombres comunes para ID idProperty = type.GetProperties().FirstOrDefault(p => p.Name.Equals("ID", StringComparison.OrdinalIgnoreCase) || // Nombre genérico 'Id' p.Name.Equals($"{type.Name}ID", StringComparison.OrdinalIgnoreCase) || // p.Name.StartsWith("ID", StringComparison.OrdinalIgnoreCase)); // Cualquier nombre que comience con 'ID' if (idProperty != null) { return idProperty.GetValue(entity); } throw new InvalidOperationException("No se pudo encontrar una propiedad de ID para la entidad."); } //protected void RegistrarActividad(string operacion, string? usuario, string? parametrosConsulta, string? respuesta = null, string? errores = null) //{ // try // { // using (var context = bdCOAS.tsCOASVD.NuevoContexto(SoloLectura: false, Lazy: false)) // { // var registro = new REGISTRODEACTIVIDADREST // { // FECHAHORA = DateTime.Now, // OPERACIONINVOCADA = operacion, // USUARIO = usuario, // PARAMETROSCONSULTA = parametrosConsulta.Length > 500 ? parametrosConsulta.Substring(0, 500) : parametrosConsulta, // RESPUESTA = respuesta, // ERRORES = errores // }; // context.Set().Add(registro); // context.SaveChanges(); // } // } // catch (Exception ex) // { // Console.WriteLine($"Error al registrar actividad: {ex.Message}"); // } //} } } public class ExpressionWrapper { public string Expression { get; set; } } public class ExpressionWrapperAtributo { public string Expression { get; set; } public string AttributeName { get; set; } }