3.3 ITracingService - Logging y Trazas

Implementa logging efectivo para depuración de plugins

Las Entity Images son uno de los conceptos que más confusión genera en desarrolladores nuevos, pero una vez las entiendes, se convierten en una herramienta indispensable. Te permiten acceder a valores del registro que de otra forma no podrías obtener, sin hacer consultas adicionales a la base de datos.

Objetivos de aprendizaje

  • Entender qué son Pre-Image y Post-Image y cuándo están disponibles
  • Configurar imágenes correctamente en el Plugin Registration Tool
  • Acceder a imágenes desde el código del plugin
  • Saber cuándo usar cada tipo de imagen según el escenario

El problema que resuelven las Entity Images

Imagina este escenario: tienes un plugin en Update de la entidad Account. Quieres detectar si el límite de crédito ha cambiado y, si es así, enviar una notificación.

El problema es que en el Update, el Target solo contiene los campos que el usuario modificó. Si el usuario cambió solo el teléfono, el campo de límite de crédito no está en el Target. Y si cambió el límite de crédito, puedes ver el nuevo valor en el Target, pero ¿cómo sabes cuál era el valor anterior?

Podrías hacer un Retrieve para leer el registro de la base de datos. Pero eso tiene problemas: consume una llamada adicional (afecta rendimiento), y en Pre-operation ya estás dentro de la transacción, así que leer el registro te daría el valor actual, no el anterior.

Aquí es donde entran las Entity Images. Son instantáneas del registro capturadas en momentos específicos del pipeline.


Pre-Image: el estado antes del cambio

Una Pre-Image es una fotografía del registro tal como estaba ANTES de que la operación comenzara. Contiene los valores originales de los campos que especifiques.

Las Pre-Images están disponibles en:

  • Update: en todos los stages (Pre-validation, Pre-operation, Post-operation)
  • Delete: en todos los stages (necesitas la Pre-Image porque después de eliminar, el registro ya no existe)

No están disponibles en Create, porque no hay un "antes" cuando el registro no existía.

Caso de uso típico: detectar cambios

Laura quiere saber si el límite de crédito de una cuenta ha cambiado, y si es así, por cuánto:


public void Execute(IServiceProvider serviceProvider)
{
    var trace = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
    var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
    
    Entity target = (Entity)context.InputParameters["Target"];
    
    // Verificar si el límite de crédito está siendo modificado
    if (!target.Contains("creditlimit"))
    {
        trace.Trace("creditlimit no fue modificado, saliendo");
        return;
    }
    
    // Obtener el nuevo valor del Target
    Money nuevoLimite = target.GetAttributeValue("creditlimit");
    decimal nuevoValor = nuevoLimite?.Value ?? 0;
    trace.Trace($"Nuevo límite: {nuevoValor}");
    
    // Obtener el valor anterior de la Pre-Image
    if (!context.PreEntityImages.Contains("preImage"))
    {
        trace.Trace("Pre-Image no configurada");
        return;
    }
    
    Entity preImage = context.PreEntityImages["preImage"];
    Money limiteAnterior = preImage.GetAttributeValue("creditlimit");
    decimal valorAnterior = limiteAnterior?.Value ?? 0;
    trace.Trace($"Límite anterior: {valorAnterior}");
    
    // Calcular la diferencia
    decimal diferencia = nuevoValor - valorAnterior;
    trace.Trace($"Diferencia: {diferencia}");
    
    if (Math.Abs(diferencia) > 10000)
    {
        // Cambio significativo, tomar acción
        trace.Trace("Cambio de crédito significativo detectado");
    }
}

Post-Image: el estado después del cambio

Una Post-Image es una fotografía del registro DESPUÉS de que la operación se completó. Contiene los valores finales de los campos especificados.

Las Post-Images solo están disponibles en Post-operation (porque en etapas anteriores, la operación todavía no ha ocurrido):

  • Create Post-operation: el registro completo con todos los valores asignados
  • Update Post-operation: el registro con los valores actualizados

Caso de uso típico: acceder al estado final completo

Carlos tiene un plugin en Post-operation de Update. Necesita conocer el nombre y el límite de crédito de la cuenta para sincronizarla con un sistema externo. El problema es que el usuario quizás solo modificó el teléfono, así que el Target no tiene ni el nombre ni el crédito.

Con una Post-Image configurada para esos campos, tiene acceso a ellos sin hacer una consulta adicional:


// En Post-operation de Update
Entity postImage = context.PostEntityImages["postImage"];

string nombre = postImage.GetAttributeValue("name");
Money credito = postImage.GetAttributeValue("creditlimit");

// Sincronizar con sistema externo
SincronizarConERP(nombre, credito);

Configurando imágenes en Plugin Registration Tool

Las imágenes no son automáticas. Debes configurarlas explícitamente al registrar el step del plugin.

En el Plugin Registration Tool:

  1. Expande tu assembly y localiza la clase del plugin
  2. Expande el step donde quieres añadir la imagen
  3. Haz clic derecho y selecciona "Register New Image"
  4. Configura los parámetros:
    • Image Type: Pre Image, Post Image, o Both
    • Name: El nombre/alias para acceder desde código (por ejemplo, "preImage")
    • Entity Alias: Generalmente igual al Name
    • Parameters: Los campos que quieres incluir (por ejemplo, "name,creditlimit,telephone1")
Buena práctica: En Parameters, especifica solo los campos que realmente necesitas. Cada campo adicional consume recursos para capturarlo. No incluyas todos los campos "por si acaso".

Nombres consistentes

Te recomiendo usar nombres estándar para tus imágenes: "preImage" para Pre-Images y "postImage" para Post-Images. Esto hace el código más predecible y reduce errores por typos.


Accediendo a imágenes desde código

Las imágenes están disponibles en el contexto:


// Pre-Image
if (context.PreEntityImages.Contains("preImage"))
{
    Entity preImage = context.PreEntityImages["preImage"];
    string valorAnterior = preImage.GetAttributeValue("name");
}

// Post-Image
if (context.PostEntityImages.Contains("postImage"))
{
    Entity postImage = context.PostEntityImages["postImage"];
    string valorFinal = postImage.GetAttributeValue("name");
}

Siempre verifica que la imagen existe antes de acceder. Si olvidaste configurarla en PRT, o la configuraste con un nombre diferente, el diccionario estará vacío.


Combinando Target con Pre-Image

Un patrón muy común es querer el valor "final" de un campo: si está siendo modificado, usar el nuevo valor del Target; si no, usar el valor actual del Pre-Image.


public static T GetFinalValue(Entity target, Entity preImage, string fieldName)
{
    // Si está en Target (fue modificado), usar ese valor
    if (target.Contains(fieldName))
    {
        return target.GetAttributeValue(fieldName);
    }
    
    // Si no, usar el valor de Pre-Image
    if (preImage != null && preImage.Contains(fieldName))
    {
        return preImage.GetAttributeValue(fieldName);
    }
    
    return default;
}

// Uso
public void Execute(IServiceProvider serviceProvider)
{
    var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
    
    Entity target = (Entity)context.InputParameters["Target"];
    Entity preImage = context.PreEntityImages.Contains("preImage") 
        ? context.PreEntityImages["preImage"] 
        : null;
    
    // Obtener el valor final sin importar si fue modificado o no
    string nombre = GetFinalValue(target, preImage, "name");
    Money credito = GetFinalValue(target, preImage, "creditlimit");
}

Cuándo usar cada tipo de imagen

Esta referencia rápida te ayuda a decidir qué imagen necesitas según tu escenario:

Quiero validar usando valores ACTUALES del registro (antes del cambio): Pre-Image

Quiero comparar valor anterior vs nuevo: Pre-Image para anterior + Target para nuevo

Quiero detectar si un campo específico cambió: Pre-Image para anterior + verificar si está en Target

Quiero el estado final del registro en Post-operation: Post-Image

Quiero datos del registro que se está eliminando: Pre-Image (obligatorio, porque después del Delete el registro no existe)

Quiero datos completos de un registro recién creado: Post-Image en Create Post-operation


Consideraciones de rendimiento

Las imágenes tienen un coste. Cada imagen requiere que Dataverse lea el registro de la base de datos y lo serialize para pasarlo a tu plugin.

Si tu plugin se registra en una entidad con muchos updates (como oportunidades en un sistema de ventas activo), y configuras una imagen con 50 campos, el coste se multiplica por miles de ejecuciones al día.

Mejores prácticas:

  • Incluye solo los campos que realmente vas a usar
  • Pregúntate si realmente necesitas la imagen o puedes trabajar solo con el Target
  • Si solo necesitas un campo, considera si vale la pena una imagen o si una consulta condicional es mejor

Puntos clave

  • Pre-Image contiene valores ANTES del cambio, disponible en Update y Delete
  • Post-Image contiene valores DESPUÉS del cambio, solo en Post-operation
  • Debes configurar las imágenes explícitamente en Plugin Registration Tool
  • Especifica solo los campos que necesitas en Parameters
  • Accede via context.PreEntityImages["nombre"] y PostEntityImages
  • Siempre verifica Contains() antes de acceder a una imagen
  • Combina Target + Pre-Image para obtener el valor "final" de cualquier campo

Para profundizar

Inicia sesión e inscríbete para guardar tu progreso.
En este curso
¿Te ha resultado útil?