Hablemos claro: lo que aprendimos con los chascos en desarrollo
¡Ala, qué lío! A veces, trabajar con GeneXus es como bailar punta: parece fácil, pero si te descuidas, te metes en tremendo enredo. Si sos de los que trabajan con procedimientos y Data Providers en GeneXus, seguro te has topado con errores que parecen sacados de un partido en la Liga Nacional: complicados, sin solución aparente y con ganas de botar la compu.
Pues no te preocupés, en esta plática vamos a revisar paso a paso cómo lidiamos con algunos problemas típicos que salieron mientras desarrollábamos y, lo mejor de todo, cómo logramos solucionarlos. ¡Así que agarrate un fresco de rosa de jamaica y pongámonos las pilas! 🥤
El problema del DataReader malcriado
El famoso error “There is already an open DataReader associated with this Command which must be closed first” es como ese compañero que te pasa interrumpiendo: no deja que el resto trabaje tranquilo. Este error aparece cuando GeneXus quiere ejecutar algo en la base de datos, pero ya tiene un lector de datos (cursor) activo. Y claro, no puede avanzar porque no sabe a quién atender primero. 😅
Este problema pasa en varios casos:
- Cuando hacés
For Each
anidados, o sea, un bucle dentro de otro. - Si intentás ejecutar un Data Provider mientras ya tenés un cursor activo.
- Cuando el procedimiento tiene demasiadas consultas concurrentes y no le da chance a la base de datos.
¿Cómo lo arreglamos?
Procesá los datos en memoria con un SDT:
Si el problema viene porque tenés consultas por todos lados, la mejor solución es traer los datos a un SDT (estructura de datos) y trabajar con ellos desde la memoria. ¡Es como llevarte las baleadas a la oficina en lugar de estar yendo a la venta a cada rato! 🍽
Cómo hacerlo:
&InvKardexCollection = DP_InvKardex(&SedeCod)
For &InvKardex in &InvKardexCollection
// Procesar los datos desde memoria
EndFor
Habilitá MARS
en el DataStore (si usás SQL Server):
Si tu base de datos es SQL Server, podés habilitar MultipleActiveResultSets
(MARS) en la cadena de conexión del DataStore. Esto le dice a SQL Server que acepte varios lectores de datos abiertos al mismo tiempo.
Server=myServer;Database=myDB;User=myUser;Password=myPassword;MultipleActiveResultSets=True;
⚠️ Importante: Cada vez que regenerés los programas en GeneXus, los cambios en la cadena de conexión desaparecerán. Por lo tanto, deberás reconfigurar manualmente este ajuste cada vez que vuelvas a generar los programas.
Cerrá cursores automáticamente con When None
:
Si un For Each
no encuentra datos, GeneXus podría dejar el cursor abierto. Para evitar esto, podés usar la cláusula When None
, que cierra el cursor si no hay registros.
Ejemplo:
For Each
Where SedeCod = &SedeCod
...
When None
// Esto cierra el cursor automáticamente
EndFor
El rollo entre Call
y Udp()
Aquí nos topamos con una de las dudas más frecuentes: ¿Cuándo usar Call
y cuándo usar Udp()
? Aunque ambos sirven para ejecutar procedimientos, tienen diferencias clave:
Aquí nos topamos con una de las dudas más frecuentes: ¿Cuándo usar Call
y cuándo usar Udp()
? Aunque ambos sirven para ejecutar procedimientos, tienen diferencias clave:
Call
:
- Se usa para procedimientos que no devuelven un valor directo.
- Permite manejar múltiples parámetros OUT (salida).
- Es perfecto para procedimientos que hacen cosas como actualizar datos o enviar correos.
Ejemplo con parámetros OUT:
Call(MiProcedimiento, &ParametroIn, &ParametroOut1, &ParametroOut2)
Udp()
:
- Se usa para procedimientos que devuelven un único valor.
- No funciona si el procedimiento tiene varios parámetros de salida.
- Es ideal para cálculos o validaciones.
Ejemplo:
&Resultado = Udp(CalcularTotal, &ParametroIn)
Nuestro caso práctico: Cálculo y actualización de existencias
Uno de los retos más interesantes fue calcular los movimientos totales de productos y actualizar las existencias tanto globales como por bodega. Aquí te va cómo lo hicimos:
Cargamos los movimientos en un SDT:
Usamos un Data Provider para obtener todos los movimientos y almacenarlos en &InvKardexCollection
.
Código:
&InvKardexCollection = DP_InvKardex(&SedeCod)
Procesamos los datos con un corte de control:
Esto nos permitió sumar las cantidades por producto y por bodega.
Código:
For &InvKardex in &InvKardexCollection
If &ProductoActual <> &InvKardex.KardexArtCod
If &ProductoActual <> ""
Do 'ActualizarExistencia'
EndIf
&ProductoActual = &InvKardex.KardexArtCod
&CantidadTotal = 0
EndIf
&CantidadTotal += &InvKardex.KardexMoviCantidad
EndFor
If &ProductoActual <> ""
Do 'ActualizarExistencia'
EndIf
Actualizamos las existencias con una subrutina:
Sub 'ActualizarExistencia'
For Each
Where POSPrdCod = &ProductoActual
POSPrdExi = POSPrdExi + &CantidadTotal
EndFor
For Each
Where InvBodegaId = &InvBodegaId
Where POSPrdCod = &ProductoActual
InvBodegaPrdCantExistencia = InvBodegaPrdCantExistencia + &CantidadTotal
EndFor
EndSub
Conclusión
¡Y listo! Con estas soluciones le bajamos los humos a los errores de GeneXus y logramos que todo caminara como mototaxi nuevo. Si te topás con estos problemas, probá con:
- Procesar los datos en memoria con un SDT.
- Habilitar
MARS
en SQL Server si usás múltiples consultas activas (pero recordá que los cambios se borrarán al regenerar los programas). - Usar correctamente
Call
yUdp()
según lo que necesités.
Ahí está todo claro, pero si te enredás, escribinos en BaleadaGeek y con gusto te damos más tips. ¡Al final, lo importante es que el sistema no se trabe como cuando te quedás sin saldo! 💪😎
Average Rating