{"id":424,"date":"2018-11-05T12:11:49","date_gmt":"2018-11-05T12:11:49","guid":{"rendered":"https:\/\/blogs.ua.es\/jpm33\/?p=424"},"modified":"2018-11-05T12:58:12","modified_gmt":"2018-11-05T12:58:12","slug":"audit-log-con-entity-framework-net","status":"publish","type":"post","link":"https:\/\/blogs.ua.es\/jpm33\/2018\/11\/05\/audit-log-con-entity-framework-net\/","title":{"rendered":"Audit Log con Entity Framework (.NET)"},"content":{"rendered":"<h1>Introducci\u00f3n<\/h1>\n<p>Cuando desarrollamos aplicaciones de una cierta envergadura debemos de plantearnos usar ciertos patrones de dise\u00f1o que garantizen la escalabilidad, robustez,\u00a0 tolerancia a fallos y auditen nuestra aplicaci\u00f3n.<\/p>\n<p>Vamos a ver c\u00f3mo usar el patr\u00f3n Audit Log para auditar los eventos y operaciones de la aplicaci\u00f3n.<\/p>\n<p>Un audit log es un registro de las acciones que ocurren sobre una aplicaci\u00f3n con unos datos (una entidad, una colecci\u00f3n, un VO, &#8230;) y registra cuando ocurre una operaci\u00f3n, quien ha sido el responsable, qu\u00e9 operaci\u00f3n ha realizado y se puede incluir la informaci\u00f3n modificada (en su estado inicial y el modificado).<\/p>\n<p>Para leer m\u00e1s sobre audit log leer el enlace adjunto de Martin Fowler.<\/p>\n<p>Otro de los problemas cubiertos en esta entrada es c\u00f3mo obtener el nombre de la tabla \/ vista de una entidad en entity framework.<\/p>\n<h1>Caso<\/h1>\n<p>Supongamos que tenemos una tabla PRODUCTOS sobre un SGBD Oracle y hemos desarrollado una aplicaci\u00f3n basada en microservicios con .NET. Adem\u00e1s hemos decidido utilizar el ORM Entity Framework como capa de abstracci\u00f3n para el acceso a datos.<\/p>\n<p>Problemas que se presentan:<\/p>\n<ul>\n<li>C\u00f3mo usamos un ORM contra un SGB, \u00bfc\u00f3mo obtener el nombre de la tabla?<\/li>\n<li>\u00bfC\u00f3mo implemento el audit log<\/li>\n<\/ul>\n<h1>Soluci\u00f3n<\/h1>\n<h1>Obtener el nombre de la tabla en EF<\/h1>\n<p>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:<\/p>\n<pre>[Table(\"PRODUCTOS\", Schema = \"MI_BASE_DE_DATOS\")]\r\n<strong>public partial class<\/strong> Producto\r\n{\r\n    [Key]\r\n    <strong>public int<\/strong> Id { <strong>get<\/strong>; <strong>set<\/strong>; }\r\n    [StringLength(100)]\r\n    <strong>public string<\/strong> Nombre { <strong>get<\/strong>; <strong>set<\/strong>; }\r\n    ...\r\n}<\/pre>\n<p>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:<\/p>\n<pre>Set(<b>typeof<\/b>(Producto)).ToString()\r\n<\/pre>\n<p>Generando el siguiente SQL:<\/p>\n<p>SELECT<br \/>\n&#8220;Extent1&#8243;.&#8221;Id&#8221; AS &#8220;Id&#8221;,<br \/>\n&#8220;Extent1&#8243;.&#8221;Nombre&#8221; AS &#8220;Nombre&#8221;,<br \/>\n&#8230;<br \/>\nFROM &#8220;MI_BASE_DE_DATOS&#8221;.&#8221;PRODUCTOS&#8221; &#8220;Extent1&#8221;<\/p>\n<p>Con lo que podemos implementar una funci\u00f3n\u00a0de utilidad para obtener el nombre de la tabla para cada entidad:<\/p>\n<pre><strong>public string<\/strong> GetTableName(<strong>Type<\/strong> entityType, <strong>bool<\/strong> debug = <strong>false<\/strong>)\r\n{\r\n  <strong>string<\/strong> sql = Set(entityType).ToString();\r\n  <strong>if<\/strong> (debug)\r\n    Console.WriteLine(\"La consulta SQL es: {0}\", sql);\r\n  <strong>Regex<\/strong> regex = <strong>new<\/strong> Regex(@\"FROM\\s+(?&lt;table&gt;.+)\\s+\");\r\n  <strong>return<\/strong> regex.Match(sql).Groups[\"table\"].Value;\r\n}\r\n<\/pre>\n<h1>Implementaci\u00f3n sencilla del auditlog<\/h1>\n<p>Para implementar el auditlog hay que tener en cuenta una cuesti\u00f3n, \u00bfqu\u00e9 sistema de loggijng usamos? Log4net, uno propio, mongodb, elasticsearch, stdout &#8230; Vamos a hacer un sencillo ejemplo con stdout (modificar el ejemplo es llamar a nuestro servicio de logging).<\/p>\n<pre><strong>public void<\/strong> auditLog(<strong>int<\/strong> userid, <strong>string<\/strong> operation, <strong>string<\/strong> table, <strong>string<\/strong> message = null)\r\n{\r\n  Console.WriteLine(string.Format(LOG, DateTime.Now, userid, operation, table, message));\r\n}<\/pre>\n<p>LOG es una constante de cadena con el formato de la l\u00ednea de log, por ejemplo:<\/p>\n<p><strong>const string LOG = &#8220;{0} &#8211; UserId = {1:D6} [{3} &#8211; {2}] &#8211; {4}&#8221;;<\/strong><\/p>\n<p>Cuya salida podr\u00eda ser:<\/p>\n<blockquote>\n<div id=\"final\"><em>05\/11\/2018 12:50:18 &#8211; UserId = 012345 [&#8220;MI_BASE_DE_DATOS&#8221;.&#8221;PRODUCTOS&#8221; &#8211; DELETE] &#8211; Se ha borrado un registro<\/em><\/div>\n<\/blockquote>\n<p>A partir de aqu\u00ed, muchas opciones, soluciones y formas de adaptar el problema a una soluci\u00f3n particular.<\/p>\n<h1>Referencias<\/h1>\n<ul>\n<li><a href=\"https:\/\/martinfowler.com\/eaaDev\/AuditLog.html\">Audit Log<\/a> (visto el 5 de nov de 2018)<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Introducci\u00f3n Cuando desarrollamos aplicaciones de una cierta envergadura debemos de plantearnos usar ciertos patrones de dise\u00f1o que garantizen la escalabilidad, robustez,\u00a0 tolerancia a fallos y auditen nuestra aplicaci\u00f3n. Vamos a ver c\u00f3mo usar el patr\u00f3n Audit Log para auditar los &hellip; <a href=\"https:\/\/blogs.ua.es\/jpm33\/2018\/11\/05\/audit-log-con-entity-framework-net\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":3080,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[676],"tags":[],"class_list":["post-424","post","type-post","status-publish","format-standard","hentry","category-sin-categoria"],"_links":{"self":[{"href":"https:\/\/blogs.ua.es\/jpm33\/wp-json\/wp\/v2\/posts\/424","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.ua.es\/jpm33\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.ua.es\/jpm33\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.ua.es\/jpm33\/wp-json\/wp\/v2\/users\/3080"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.ua.es\/jpm33\/wp-json\/wp\/v2\/comments?post=424"}],"version-history":[{"count":3,"href":"https:\/\/blogs.ua.es\/jpm33\/wp-json\/wp\/v2\/posts\/424\/revisions"}],"predecessor-version":[{"id":428,"href":"https:\/\/blogs.ua.es\/jpm33\/wp-json\/wp\/v2\/posts\/424\/revisions\/428"}],"wp:attachment":[{"href":"https:\/\/blogs.ua.es\/jpm33\/wp-json\/wp\/v2\/media?parent=424"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.ua.es\/jpm33\/wp-json\/wp\/v2\/categories?post=424"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.ua.es\/jpm33\/wp-json\/wp\/v2\/tags?post=424"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}