3.4 Manejo de Imágenes (Pre y Post)

Usa imágenes para acceder a valores antes y después del cambio

🎯 Objetivos de aprendizaje

  • Manejar excepciones correctamente en plugins
  • Usar InvalidPluginExecutionException para cancelar operaciones
  • Implementar validaciones de negocio efectivas
  • Entender el comportamiento de rollback

📚 InvalidPluginExecutionException

La InvalidPluginExecutionException es la forma correcta de cancelar una operación y mostrar un mensaje al usuario:


// Lanzar excepción para cancelar operación
throw new InvalidPluginExecutionException(
    "No se puede procesar: el cliente tiene crédito bloqueado. " +
    "Contacte al departamento de finanzas.");

Opciones del constructor


// Solo mensaje
throw new InvalidPluginExecutionException("Mensaje de error");

// Mensaje con inner exception (para diagnóstico)
catch (Exception ex)
{
    throw new InvalidPluginExecutionException(
        "Error procesando el pedido", ex);
}

// Con código de error personalizado
throw new InvalidPluginExecutionException(
    OperationStatus.Failed,
    12345,  // Tu código de error personalizado
    "Error de procesamiento");

⚡ Patrón de validación completo


public class ValidacionPedidoPlugin : IPlugin
{
    public void Execute(IServiceProvider serviceProvider)
    {
        var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
        var trace = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
        var factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
        var service = factory.CreateOrganizationService(context.UserId);
        
        try
        {
            Entity target = (Entity)context.InputParameters["Target"];
            
            // Lista de errores
            List<string> errores = new List<string>();
            
            // Validación 1: Campo requerido
            if (string.IsNullOrEmpty(target.GetAttributeValue<string>("name")))
            {
                errores.Add("El nombre del pedido es obligatorio");
            }
            
            // Validación 2: Valor mínimo
            decimal monto = target.GetAttributeValue<Money>("totalamount")?.Value ?? 0;
            if (monto < 100)
            {
                errores.Add("El monto mínimo del pedido es $100");
            }
            
            // Validación 3: Consulta a BD
            EntityReference clienteRef = target.GetAttributeValue<EntityReference>("customerid");
            if (clienteRef != null)
            {
                Entity cliente = service.Retrieve(clienteRef.LogicalName, clienteRef.Id,
                    new ColumnSet("creditonhold"));
                
                if (cliente.GetAttributeValue<bool>("creditonhold"))
                {
                    errores.Add("El cliente tiene el crédito bloqueado");
                }
            }
            
            // Si hay errores, lanzar excepción con todos
            if (errores.Count > 0)
            {
                string mensajeCompleto = string.Join("\n• ", errores);
                throw new InvalidPluginExecutionException(
                    $"No se puede crear el pedido:\n• {mensajeCompleto}");
            }
            
            trace.Trace("Todas las validaciones pasaron");
        }
        catch (InvalidPluginExecutionException)
        {
            throw; // Re-throw validaciones de negocio
        }
        catch (Exception ex)
        {
            trace.Trace($"Error inesperado: {ex}");
            throw new InvalidPluginExecutionException(
                $"Error procesando el pedido: {ex.Message}", ex);
        }
    }
}

🔄 Rollback automático

StageRollback si fallaComportamiento
Pre-validation (10)N/ANo hay transacción aún, la operación simplemente no continúa
Pre-operation (20)✅ SíTodo lo dentro de la transacción se revierte
Post-operation Sync (40)✅ SíLa operación principal Y acciones del plugin se revierten
Post-operation Async❌ NoLa operación ya se confirmó, el plugin falla independientemente

// Ejemplo: Plugin en Post-operation síncrono
public void Execute(IServiceProvider serviceProvider)
{
    // ... setup ...
    
    // Esta cuenta ya "existe" en la BD (dentro de la transacción)
    Guid nuevaCuentaId = context.PrimaryEntityId;
    
    // Crear contacto relacionado
    Entity contacto = new Entity("contact");
    contacto["parentcustomerid"] = new EntityReference("account", nuevaCuentaId);
    contacto["lastname"] = "Gerente";
    Guid contactoId = service.Create(contacto);  // Se crea dentro de la transacción
    
    // Si AQUÍ lanzamos excepción...
    throw new InvalidPluginExecutionException("Fallo intencional");
    
    // RESULTADO: Ni la cuenta ni el contacto existirán
    // Todo se revierte porque estamos dentro de la transacción
}

⚠️ Errores comunes


// ❌ MAL: Tragarse excepciones
try
{
    service.Create(registro);
}
catch (Exception ex)
{
    trace.Trace($"Error: {ex.Message}");
    // SIN throw - el plugin termina "exitosamente" pero la operación falló silenciosamente
}

// ❌ MAL: Lanzar Exception genérica
throw new Exception("Error");  // No es user-friendly

// ✅ BIEN: Re-throw o lanzar InvalidPluginExecutionException
try
{
    service.Create(registro);
}
catch (FaultException<OrganizationServiceFault> ex)
{
    throw new InvalidPluginExecutionException(
        $"No se pudo crear el registro: {ex.Detail.Message}", ex);
}

📝 Resumen

  • Usa InvalidPluginExecutionException para cancelar operaciones
  • El mensaje se muestra al usuario - hazlo claro y útil
  • Acumula errores y muestra todos juntos si hay múltiples
  • Rollback es automático en Pre-op y Post-op síncrono
  • En async, la operación principal ya se confirmó
  • Nunca "tragues" excepciones sin re-throw
Inicia sesión e inscríbete para guardar tu progreso.
En este curso
¿Te ha resultado útil?