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:
- Expande tu assembly y localiza la clase del plugin
- Expande el step donde quieres añadir la imagen
- Haz clic derecho y selecciona "Register New Image"
- 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")
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