# 9.6 Guardando el historial de cambios

Puedes agregar otro action filter para ir registrando todos los cambios que realizan los usuarios al sistema. Vamos a registrar el usuario, la fecha en que se realiza el cambio, y un campo adicional para agregar opciones adicionales, como por ejemplo mostrar el valor anterior.&#x20;

Agregamos nuestra clase **HistorialDAO** para registrar el historial de los usuarios. El método borrar va a borrar la información del historial del registro que se esta borrando.

{% code title="HistorialDAOcs" %}

```csharp
public class HistorialDAO
{
    private readonly CaducaContext contexto;
    private readonly LocService localizacion;

    public HistorialDAO(CaducaContext context, LocService locService)
    {
        this.contexto = context;
        this.localizacion = locService;
    }

    public async Task<bool> AgregarAsync(int usuarioId, 
                                            int actividad,
                                            string nombreTabla, 
                                            int origenId,
                                            string observaciones)
    {
        var tablaId = await ObtenerIdTablaAsync(nombreTabla);
        Historial historial = new Historial
        {
            Actividad = actividad,
            FechaHora = DateTime.Now,
            Observa = observaciones,
            OrigenId = origenId,
            TablaId = tablaId,
            UsuarioId = usuarioId
        };
        contexto.Historial.Add(historial);
        contexto.SaveChanges();
        return true;
    }

    private async Task<int> ObtenerIdTablaAsync(string nombreTabla)
    {
        Tabla tabla = await contexto.Tabla
                .FirstOrDefaultAsync(e => e.Nombre == nombreTabla);
        if (tabla == null)
        {
            return -1;
        }
        return tabla.Id;
    }
    
    public async Task<bool> BorraAsync( string nombreTabla, 
                                         int origenId)
    {
        var tablaId = await ObtenerIdTablaAsync(nombreTabla);
        var consulta = await (from historial in contexto.Historial
                        where historial.TablaId == tablaId
                        && historial.OrigenId == origenId                 
                        select historial).ToListAsync();
        contexto.Historial.RemoveRange(consulta);
        contexto.SaveChanges();
        return true;
    }
}
```

{% endcode %}

Agregamos una Enumeración **ActividadEnumeration** con los tipos de actividades que los usuarios pueden realizar.

{% code title="ActividadEnumeration.cs" %}

```csharp
public enum ActividadEnumeration
{
        Borrar      = -1,
        Insertar    = 1,
        Modificar   = 2,
        Autorizar   = 3,
        Cancelar    = 4 
}
```

{% endcode %}

Para esto agregamos una propiedad llamada **Id** de tipo **entero** a nuestra clase **BaseController,** aqui vamos a agregar el Id de cada registro que se este actualizando

{% code title="BaseController.cs" %}

```csharp
public class BaseController : Controller
{
    public int Id;
    public string Observaciones;
}
```

{% endcode %}

En **ProductosController** a los métodos Post, Put y Delete vamos a agregar el Id del registro y agregamos nuestro filtro historial

{% code title="ProductosController.cs" %}

```csharp
[TypeFilter(typeof(HistorialFilter))]
public class ProductosController : BaseController
{
    [HttpPost]
    public async Task<IActionResult> PostProducto(
                                       [FromBody] Producto producto)
    {
        if (!await productoDAO.AgregarAsync(producto))
        {
            return StatusCode(productoDAO.customError.StatusCode,
                                  productoDAO.customError.Message);
        }
        Id = producto.Id;
        return CreatedAtAction("GetCategoria", 
                              new { id = producto.Id }, producto);
    }
    
    [HttpPut("{id}")]
    public async Task<IActionResult> PutProducto([FromRoute] int id, 
                                        [FromBody] Producto producto)
    {
        if (!ModelState.IsValid)
            return BadRequest(ModelState);

        if (id != producto.Id)
             return BadRequest();

        if (!await productoDAO.ModificarAsync(producto))
        {
            return StatusCode(productoDAO.customError.StatusCode,
                                  productoDAO.customError.Message);
        }
        Id = producto.Id;
        return NoContent();
    }
    
    [HttpDelete("{id}")]
    public async Task<IActionResult> DeleteProducto([FromRoute] int id)
    {
        if (!ModelState.IsValid)
            return BadRequest(ModelState);
        if (!await productoDAO.BorraAsync(id))
        {
                return StatusCode(productoDAO.customError.StatusCode,
                                  productoDAO.customError.Message);
        }
        Id = id;
        return Ok();
    }
```

{% endcode %}

En nuestra carpeta Filters agregamos un nueva clase **HistorialFilter**

{% code title="HistorialFilter.cs" %}

```csharp
using System;
using System.Linq;
using System.Security.Claims;
using CaducaRest.Controllers;
using CaducaRest.Core;
using CaducaRest.DAO;
using CaducaRest.Models;
using CaducaRest.Resources;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

namespace CaducaRest.Filters;

public class HistorialFilter : IActionFilter
{
    private readonly LocService _localizer;
    private readonly CaducaContext _context;

    public HistorialFilter(CaducaContext context, 
                           LocService localizer)
    {
        this._localizer = localizer;
        this._context = context;
    }
    //Este método se ejecuta al terminar el código de cada
    //método
    public void OnActionExecuted(ActionExecutedContext context)
    {
        bool correcto = false;
        //Si la acción se realiza correctamente
        correcto = context.Result is OkObjectResult
            || context.Result is NoContentResult
            || context.Result is CreatedAtActionResult;

        if (correcto)
        {
            ClaimsPrincipal Usuario = context.HttpContext.User;
            //Obtenemos nuestro controlador
            var controller = context.Controller as BaseController;
            //Obtenemos el método que se ejecutó (POST, PUT, 
            //DELETE, etc)
            string metodo = controller.Request.Method.ToLower();
            //Identificamos de acuerdo al método la acción
            //realizada
            int actividad;
            switch (metodo)
            {
                case "post":
                    actividad = (int)ActividadEnumeration.Insertar;
                    break;
                case "put":
                case "patch":
                    actividad = (int)ActividadEnumeration.Modificar;
                    break;
                case "delete":
                    actividad = (int)ActividadEnumeration.Borrar;
                    break;
                default:
                    actividad = 0;
                    break;
            }
            int UsuarioId = 0;
            if (correcto)
            {
                int.TryParse(Usuario.Claims
                  .FirstOrDefault(x => x.Type == ClaimTypes.Sid)
                  .Value, out UsuarioId);
                if (UsuarioId > 0 && controller.Id > 0)
                {
                    //Si es una acción de alta o edición
                    //agregamos un registro al historial
                    HistorialDAO historialDAO = 
                         new HistorialDAO(_context, _localizer);
                    if (actividad > 0)
                        _ = historialDAO.AgregarAsync(UsuarioId,
                                  actividad,
                                  controller.permiso.Tabla,
                                  controller.Id,
                                  controller.Observaciones).Result;
                    else
                    {
                        //Borramos el historial del registro
                        if (actividad < 0)
                            _ = historialDAO
                            .BorraAsync(controller.permiso.Tabla,
                                     controller.Id).Result;
                    }
                }
            }
        }
    }

    public void OnActionExecuting(ActionExecutingContext context)
    {
    }
}
```

{% endcode %}

De esta manera cada vez que se guarda, modifica, o borra algún registro guardamos el historial de cambios del registro.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://abi.gitbook.io/net-core/7.-seguridad/7.14-guardando-el-historial-de-cambios.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
