Audit Log con Entity Framework (.NET)

Introducción

Cuando desarrollamos aplicaciones de una cierta envergadura debemos de plantearnos usar ciertos patrones de diseño que garantizen la escalabilidad, robustez,  tolerancia a fallos y auditen nuestra aplicación.

Vamos a ver cómo usar el patrón Audit Log para auditar los eventos y operaciones de la aplicación.

Un audit log es un registro de las acciones que ocurren sobre una aplicación con unos datos (una entidad, una colección, un VO, …) y registra cuando ocurre una operación, quien ha sido el responsable, qué operación ha realizado y se puede incluir la información modificada (en su estado inicial y el modificado).

Para leer más sobre audit log leer el enlace adjunto de Martin Fowler.

Otro de los problemas cubiertos en esta entrada es cómo obtener el nombre de la tabla / vista de una entidad en entity framework.

Caso

Supongamos que tenemos una tabla PRODUCTOS sobre un SGBD Oracle y hemos desarrollado una aplicación basada en microservicios con .NET. Además hemos decidido utilizar el ORM Entity Framework como capa de abstracción para el acceso a datos.

Problemas que se presentan:

  • Cómo usamos un ORM contra un SGB, ¿cómo obtener el nombre de la tabla?
  • ¿Cómo implemento el audit log

Solución

Obtener el nombre de la tabla en EF

En Entity Framework, al definir una clase como una entidad de base de datos se anota con el decorador Table para indicar la tabla de base de datos (sino se llama igual que la clase), por ejemplo:

[Table("PRODUCTOS", Schema = "MI_BASE_DE_DATOS")]
public partial class Producto
{
    [Key]
    public int Id { get; set; }
    [StringLength(100)]
    public string Nombre { get; set; }
    ...
}

Esta clase representa nuestro producto mapeado em la base de datos. Al ser una entidad, si hacesmos un ToString() sobre su tipo, obtenemos el SQL que infiere Entity Framework para realizar consultas:

Set(typeof(Producto)).ToString()

Generando el siguiente SQL:

SELECT
“Extent1″.”Id” AS “Id”,
“Extent1″.”Nombre” AS “Nombre”,

FROM “MI_BASE_DE_DATOS”.”PRODUCTOS” “Extent1”

Con lo que podemos implementar una función de utilidad para obtener el nombre de la tabla para cada entidad:

public string GetTableName(Type entityType, bool debug = false)
{
  string sql = Set(entityType).ToString();
  if (debug)
    Console.WriteLine("La consulta SQL es: {0}", sql);
  Regex regex = new Regex(@"FROM\s+(?<table>.+)\s+");
  return regex.Match(sql).Groups["table"].Value;
}

Implementación sencilla del auditlog

Para implementar el auditlog hay que tener en cuenta una cuestión, ¿qué sistema de loggijng usamos? Log4net, uno propio, mongodb, elasticsearch, stdout … Vamos a hacer un sencillo ejemplo con stdout (modificar el ejemplo es llamar a nuestro servicio de logging).

public void auditLog(int userid, string operation, string table, string message = null)
{
  Console.WriteLine(string.Format(LOG, DateTime.Now, userid, operation, table, message));
}

LOG es una constante de cadena con el formato de la línea de log, por ejemplo:

const string LOG = “{0} – UserId = {1:D6} [{3} – {2}] – {4}”;

Cuya salida podría ser:

05/11/2018 12:50:18 – UserId = 012345 [“MI_BASE_DE_DATOS”.”PRODUCTOS” – DELETE] – Se ha borrado un registro

A partir de aquí, muchas opciones, soluciones y formas de adaptar el problema a una solución particular.

Referencias

Deja un comentario

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