Desactivación del flag para anidamiento de DLLS en versión 14

Aplica a: a3ERP
    +1
  • a3ASESOR | business 360

En este artículo tratamos los siguientes puntos:

 

 

276497 - arrows chevron double outline Introducción - Las DLLs de terceros en a3ERP

Como ya sabemos, son la manera que ofrece a3ERP para extender la funcionalidad de sus entidades. Funcionan de forma parecida a los eventos de la programación orientada a objeto. En determinadas partes del ERP, se ‘simulan’ unos eventos que permiten engancharse a programaciones externas. Así se puede extender el ERP con funcionalidades que particularizan o amplían la que ya viene implementada de serie.

En versiones antiguas de a3ERP, aunque se podían tener varias DLL de fabricantes distintos, sólo se ejecutaba una DLL, la primera que implementara el evento. Esto limitaba mucho la extensibilidad, pero evitaba el problema de anidamiento al que nos referiremos más tarde.

 

276497 - arrows chevron double outline Las DLLs

A partir de la versión 11, cuando se desencadena un evento de DLL, se ejecutan todas las implementaciones de ese evento en todas las DLLs que estén disponibles. El orden de ejecución se puede controlar mediante el campo ’ORDENEJECUCION’, en la tabla ‘DLLS’.

De esta forma, se asegura que todas las funcionalidades y reglas de negocio implementadas en DLL de terceros se ejecutan siempre para todas las entidades.

 

276497 - arrows chevron double outline Recursión implícita

Supongamos que, dentro de un evento, realizamos una acción que vuelve a desencadenar el mismo evento. Un caso típico es cuando en un documento recién modificado por el usuario, implementamos un evento ‘DespuesDeGuardarDocumento’ para modificarlo utilizando a3ERPActiveX, recalcular campos de diccionario, etc.

Al ejecutar el método ‘Anade’, se vuelve a grabar el documento y, por lo tanto, se vuelve a desencadenar un evento ‘DespuesDeGuardarDocumento’. No sólo en la DLL que ha realizado el cambio, sino en todas, pues debe asegurarse que el documento cumple las reglas de negocio implementadas en todas las DLL.

Llamaremos a este efecto ‘recursión implícita’. Aunque no hemos hecho una llamada ‘explícita’ al evento, nuestras acciones han desencadenado una.

 

276497 - arrows chevron double outline Inhibición de la anidación de eventos

En las versiones 11 a 13 se permitía utilizar las DLLs de terceros a la manera antigua. Para ello, se podía insertar en la tabla ‘__FLAGS’ el valor ‘CONTROLAR_RECURSION_DLLS’:

  • INSERT INTO __FLAGS VALUES (CONTROLAR_RECURSION_DLLS, 1)

Esta opción permitió una transición suave de un modelo a otro. Se ha podido seguir usando la programación a medida tal como estaba, mientras se daba tiempo a modificarla para adaptarse al modelo actual.

A partir de la versión 14 de a3ERP, se elimina el flag ‘CONTROLAR_RECURSION_DLLS’. Las DLL externas sólo funcionarán ejecutando todos los eventos en todas las DLL.

 

183684 - arrow move outline rightA tener en cuenta: toda programación a medida que no esté adaptada puede dejar de funcionar en la versión 14 de a3ERP.

 

276497 - arrows chevron double outline Detectando la recursión implícita

Una herramienta práctica y fácil de utilizar es el log de DLL que provee a3ERP.

En este log se anota cada vez que se entra y se sale de un evento de DLL, con información de día y hora. De esta forma, es fácil comprobar en qué eventos el programa cae en bucles.

Para activar esta funcionalidad, es necesario añadir las siguientes entradas en el fichero CONFIGURACION.INI ubicado en %APPDATA%\A3:

log1

 

276497 - arrows chevron double outline Recursividad implícita – Evitando bucles infinitos

La recursividad implícita (y normalmente no deseada) no es diferente de la recursividad ‘a propósito’.

En todo algoritmo recursivo, hay que proveer siempre una salida no recursiva. Es lo que mostraremos para los escenarios más comunes.

La recursión puede producirse, principalmente, de tres maneras:

  • Provocada por el propio evento que se está invocando. En el evento hacemos algo que vuelve a invocar el evento.
  • Provocada por eventos cruzados. El evento 1 desencadena al evento 2 y, este último, hace algo que vuelve a desencadenar el 1. Y así ad infinitum.
  • Cualquiera de los dos casos anteriores, pero los eventos pertenecen a diferentes DLLs.
    Por ejemplo, el evento 1 de la DLL-1 desencadena el evento 1 de la DLL-2 y, este último, vuelve a provocar el disparo del evento 1 en DLL-1.

El problema puede darse tanto en eventos de maestro como de documento. Ambos casos se resuelven igualmente.

 

276497 - arrows chevron double outline Recursión provocada por el mismo evento.

Es el caso más simple, y seguramente el más frecuente.

Supongamos este caso:

recursion_imagen

Se utiliza un objeto de tipo ‘TRowObject’, que facilita recuperar campos del parámetro ‘Datos’. El código Delphi está disponible en este enlace:

Ayuda para crear eventos: Delphi snippet - RowObject

 

El uso del ‘guarda’ del objeto maestro desencadena los eventos de antes y después de guardar maestro. Tal como está, la DLL provocaría un bucle infinito.

Vamos a ver dos maneras de solucionarlo. Cada una tiene ventajas e inconvenientes.

 

Utilizar una lista de identificadores

Cuando entramos en el procedimiento, consultamos una lista donde almacenamos los identificadores de los registros que ya se han procesado en el evento. Si ya está en la lista, se abandona el proceso. Si no está, lo añadimos a la cola y seguimos con lo que haga el evento.

Después, en el mismo evento, cuando se ha terminado todo el proceso, se elimina el identificador de la lista.

A continuación, se detallan los cambios que hay que realizar en el código original.

En color marrón, las diferencias con la versión original.

imagen_identificadores

 

La ventaja de esta solución es su estandarización. Es decir, se hace de manera muy parecida, en todo tipo de eventos. Puede aplicarse mecánicamente, de un programa a otro.

Como inconveniente, en escenarios más complejos, que veremos más adelante, podría no garantizar que se apliquen las reglas de negocio, o que se apliquen de forma incorrecta.

 

Comprobar si las reglas de negocio están ya aplicadas

En este caso, al entrar al evento, se comprueba si ya se han aplicado los cambios que se pretenden realizar. Si es así, se sale del evento. Si no, se le da el curso normal. Es decir, usamos el propio cambio como semáforo.

En nuestro ejemplo, se comparará el campo ‘PARAM1’ con la fecha del día:

 param1

 

Este método asegura que siempre se apliquen las reglas de negocio. Si los campos de la entidad no tienen los valores correctos, el evento se ejecutará.

El inconveniente es que, para cada caso, el código cambia. Cada comprobación dependerá de los cambios que el evento realice.

Además, comprobar las reglas de negocio puede ser mucho más complicado que comparar un campo con un valor fijo.

 

276497 - arrows chevron double outline Recursión provocada por eventos ‘cruzados’

Es un caso parecido al anterior, pero la recursión se desencadena en otro evento que, a su vez, realiza una acción que vuelve a llamar al primer evento. Veamos este caso.

En un evento ‘DespuesDeGuardarDocumento’, para las facturas de venta, guardamos en el cliente en un campo parametrizable la fecha de la última factura dado de alta.

A la vez, en el evento ‘DespuesDeGuardarMaestro’, para un cliente cuando se guarda se comprueba si se han facturado más de mil euros. Si es así, se hace una factura de bonificación y se marca en un parametrizable que se ha realizado la bonificación.

No es necesario detallar el código para ver que, en este caso ficticio, se va a producir una recursión implícita, aunque no la estemos provocando desde el mismo evento.

Las dos soluciones explicadas anteriormente servirían para este caso, con la precaución de realizarlas en ambos eventos.

 

276497 - arrows chevron double outline Recursión provocada por eventos en otras DLLs

En este caso, la recursión se produce entre eventos de diferentes DLLs.

Supongamos que tenemos dos DLLs, DllUno y DllDos. Ambas implementan el método DespuesdeGuardarDocumentoV2.

DllUno:

En cada factura insertamos dos nuevas líneas de ajuste.

dll1

DllDos:

Cambia el valor de dos campos parametrizables, en la cabecera de la factura.

dll2

 

Tal como están, ambas DLLs por su cuenta generarían un anidamiento infinito.

Si probamos a utilizar la lista de identificadores podemos comprobar que, efectivamente, el programa deja de caer en un bucle infinito.

Pero al comprobar la factura, vemos que la factura tiene cuatro líneas de ajuste, en lugar de dos.

¿Cómo es posible? Lo que ha pasado es un pequeño lío de llamadas a ambas DLLs, que trataremos de explicar a continuación.

Cuando el usuario guarda la factura, se disparan las dos DLL por orden. La DllUno pone el semáforo-1 a true, hace lo que tiene que hacer y guarda la factura.

Este guardado desencadena el proceso de las DLLs desde el principio otra vez. Recordad que todas las DLL deben volver a ejecutarse para garantizar las reglas de negocio. Entramos en un segundo ciclo de llamadas.

El evento de la DllUno vuelve a ejecutarse. Encuentra el semáforo a true, y sale sin hacer nada.

La DllDos ejecuta su evento. El semáforo-2 está a false, lo pone a true, hace lo suyo y vuelve a guardar. Y esto, efectivamente, vuelve a desencadenar todo el proceso de eventos de DLL. Es decir, hay un tercer ciclo de llamadas.

Ahora, ambas DLL llaman a sus eventos, pero en ambas el semáforo está a true. Salen sin hacer nada, y se da por cerrado el ciclo de llamadas número tres.

El control vuelve al ciclo de llamadas número dos. La DllDos pone el semáforo-2 a False, y termina.

Estamos ahora en el ciclo de llamadas inicial. Recuperamos el control justo después del ‘Anade’, en la DllUno. Ponemos el semáforo-1 a false y termina.

Ahora, el ciclo de llamadas pasa por la DllDos, la cual pone de nuevo el semáforo-2 a True, y hace lo que está previsto. Vuelve a guardar su documento… y vuelve a generar otro ciclo de llamadas.

Este repetirá la llamada al evento de la DllUno, pero ahora el semáforo-1 está a false. Lo pone a True y ejecuta todo el resto del código, vuelve a grabar las líneas de ajuste en el documento y se desencadena otro ciclo de llamadas. Este ciclo se extingue sin hacer nada, pues ambos semáforos están a true.

Los ciclos a partir de ahora se extinguen naturalmente por semáforos cerrados o porque terminan normalmente, liberan los semáforos y terminan su ejecución.

El lector se estará preguntando:

> ¿No se podía tomar algún atajo y no lanzar los ciclos desde el principio a partir del segundo ciclo?

La respuesta es que esto no es posible. Cada vez que se modifica un documento, todos los eventos pertinentes deben lanzarse. Se pueden realizar validaciones (sobre todo en los ‘AntesDeGuardar’) que deben asegurarse cada vez que modificamos. Además, los cambios pueden implicar que otros eventos se comporten de manera diferente.

 

En este caso, sólo es posible solucionarlo mediante la comprobación de las reglas de negocio.

Una implementación posible sería utilizando también el evento ‘AntesDeGuardarDocumento’. En este evento, comprobamos si hemos realizado los cambios. Si no lo hemos hecho, añadimos el identificador a la lista de los ejemplos anteriores. Esta lista ha cambiado de funcionalidad. Ahora indica los documentos que necesitan procesarse, en lugar de los que se excluyen.

En el evento ‘DespuesDeGuardarDocumento’, basta comprobar si el identificador está en la lista. Si es así, damos curso al evento y al final lo borramos de la lista.

Combinando los dos eventos, evitamos tener que hacer búsquedas en base de datos en el ‘DespuesDeGuardar’, lo que es más óptimo.

antes de guardar documento

Podemos comprobar que con estos cambios en la DllUno bastaría para que ahora todo funcione correctamente. La DllDos se puede dejar controlada por una lista de identificadores. Pero por coherencia del código, es conveniente aplicar la misma metodología en ambas.

 

276581 - add bookmark favorite new out Consejos finales

La manera más fácil de evitar la recursión implícita es evitar hacer modificaciones a través de a3ERPActiveX de las mismas entidades que han desencadenado el evento. En las versiones vigentes de a3ERP, es posible modificar los valores de parte de las entidades en los eventos del tipo ‘AntesDeGuardar’. Siempre que sea posible, se debe utilizar esta solución. Esto proporciona tanto un código más rápido como evitar posibles anidamientos.

Para evitar sorpresas cuando se combinan programaciones a medida de varios fabricantes, cada una por separado debería protegerse de la recursión implícita por sistema. No asumir nunca que nuestro código va a trabajar en solitario puede evitar problemas en el futuro, tanto por nuevas DLL de otros desarrolladores como de futuras implementaciones propias.

Además, una buena práctica sería acompañar la DLL de terceros con una documentación que explique lo que implementa, haciendo hincapié en qué entidades se modifican a través de a3ERPActiveX. Con ello otros desarrolladores pueden averiguar los posibles puntos de fricción con su código.

 

    • ¿Te ha resultado útil esta página?
    • ¡Gracias por votar!
    • 2 valoraciones: 3.5 sobre 4 de media
    • ¿Te ha resultado útil esta página?
    • ¡Gracias por votar!
    • 2 valoraciones: 3.5 sobre 4 de media

Palabras clave